Quickstart
From nothing to an alert on your phone in five steps. You need a terminal, and the iOS app if you want the phone to ring.
Create an account and a tenant
Sign up in the iOS app, or from the API. Signing up gives you a login; it does not create a tenant, so create one next. A tenant is a company or a team, and everything below lives inside it.
PT=https://api.pingtower.com curl -s "$PT/v1/signup" -H 'Content-Type: application/json' \ -d '{"email":"[email protected]","password":"…"}' # → {"account":{…},"token":"pts_…"} curl -s "$PT/v1/tenants" -H "Authorization: Bearer pts_…" \ -H 'Content-Type: application/json' -d '{"name":"Acme"}' # → {"id":"<tid>","name":"Acme","role":"owner"}Mint a tenant API key
The key starts with
ptk_, grants the whole data plane for that tenant, and is shown once. Treat it like a password.curl -s -X POST "$PT/v1/tenants/<tid>/keys" -H "Authorization: Bearer pts_…" # → {"id":"…","token":"ptk_<tenant>_…"}Create a project and a source
A project groups the things you monitor. A source is one thing that sends: an app, a host, a probe. Creating a source mints its ingest token, also shown once.
retain_logskeeps the raw lines so you can read them behind an alert.API=ptk_… curl -s "$PT/v1/projects" -H "Authorization: Bearer $API" -d '{"name":"checkout"}' curl -s "$PT/v1/projects/checkout/sources" -H "Authorization: Bearer $API" \ -d '{"name":"api","retain_logs":true}' # → {"name":"api","token":"pti_<tenant>_…"}Add a rule
This one opens an alert on the first error-level line and re-pages every fifteen minutes until someone acks.
curl -s "$PT/v1/projects/checkout/rules" -H "Authorization: Bearer $API" \ -d '{"name":"errors","match":{"min_level":400},"renotify_minutes":15}'Send a line and watch it become an alert
Ingest takes the source token, not the API key. The response tells you whether a rule matched.
curl -s "$PT/v1/ingest" -H "Authorization: Bearer pti_<tenant>_…" \ -d '{"message":"payment gateway timeout","level":400, "keys":{"order":"o_48113","gateway":"stripe"},"tags":["billing"]}' # → 202 {"alerted":true,"template_id":"…"}body, _ := json.Marshal(map[string]any{ "message": "payment gateway timeout", "level": 400, "keys": map[string]any{"order": "o_48113", "gateway": "stripe"}, "tags": []string{"billing"}, }) req, _ := http.NewRequest("POST", "https://api.pingtower.com/v1/ingest", bytes.NewReader(body)) req.Header.Set("Authorization", "Bearer "+os.Getenv("PINGTOWER_INGEST_TOKEN")) req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req)import os, requests requests.post( "https://api.pingtower.com/v1/ingest", headers={"Authorization": "Bearer " + os.environ["PINGTOWER_INGEST_TOKEN"]}, json={"message": "payment gateway timeout", "level": 400, "keys": {"order": "o_48113", "gateway": "stripe"}, "tags": ["billing"]}, timeout=5, )The alert is now in the feed. In the app, tap it for the count, the level, first and last seen, and the line that fired it; swipe to ack. From a terminal:
curl -s "$PT/v1/pull?cursor=0" -H "Authorization: Bearer $API" curl -s -X POST "$PT/v1/alerts/<id>/ack" -H "Authorization: Bearer $API"
min_level: 400 means “error and worse”. Anything that can POST JSON can be a source; put variable data in keys so repeats collapse into one alert with a count.