Concepts
Nine nouns. Everything else in the docs is built out of these.
| Term | What it is | Lives in |
|---|---|---|
| Account | You. An email and a password. One account can belong to many tenants. | pingtower.com |
| Tenant | A company or a team. Owns its own data, members and API keys. Every tenant you join gives you a separate credential scoped to just that tenant’s data. | pingtower.com, or your own box |
| Project | A namespace inside a tenant: a service, an environment, a product. Holds sources, checks, rules, integrations, actions and maintenance windows. Open-alert counts roll up per project. Addressed by name. | tenant |
| Source | One thing that sends lines. Each source has its own ingest token, which you can rotate without touching the others, and its own retain_logs switch. | project |
| Template | A message’s shape: the line with the values you passed as keys substituted out. payment failed for o_48113 with keys.order = o_48113 becomes payment failed for {{ .order }}, so every order’s failure counts as the same thing. | project |
| Rule | A declarative match plus timing: what to look for, how many in what window, how often to re-page, who to escalate to. | project |
| Check | Something Pingtower tests on a schedule: a website, an API journey, a browser script, a port, a certificate, a domain. On pingtower.com it runs from Pingtower’s own servers; otherwise on your agents. Its results arrive as events like any other. | project |
| Alert | A rule that fired. Carries a count, an escalation level, first and last seen, and the last message. Firing until acked, resolved when done. | project |
| Incident | The team’s record of one outage: numbered INC-n, with the alerts it gathered, a timeline, and a cause and fix once resolved. What a status page shows your customers. | tenant |
How isolation works
Each tenant is its own database: a PostgreSQL schema, or a SQLite file. The credential you present decides which one is opened. Nothing in a URL or a request body can reach another tenant’s data, because there is no code path that opens a database the token did not name.
Projects are namespaces within that database. A project name in a path is a filter, never a second identity check.
Roles
Each tenant membership has one role. Five are built in, listed here from most to least access. An owner can add custom roles alongside them.
| Role | Can |
|---|---|
| owner | Everything, including deleting the tenant and setting its plan. A tenant always has at least one; removing the last one is refused. |
| admin | Manages the tenant alongside the owner: invite and remove members, revoke other members’ API keys, rename the tenant, rename or delete a project, delete a source, delete a report (incident), rotate a source token, revoke a pairing. |
| member | Everything on the data plane with their own API key: read and act on alerts, create and edit sources, rules, integrations, actions and reports; deleting a report needs an owner or admin. Mint and revoke their own API keys. |
| responder | Work incidents without touching configuration: read everything, acknowledge and resolve alerts, mute (snooze) a rule, work incidents (open and edit them, attach and detach alerts, add and edit internal notes, accept, resolve and reopen them, link issues), and take on-call overrides for themselves. Public incident updates and reports stay with members and above. Mint and revoke their own API keys. |
| viewer | Read everything, change nothing. They can still mint and revoke their own API keys, register their own phone for push, and approve their own AI-connector consent. A viewer cannot be an on-call participant. |
Only an owner, an admin or a custom role with members:write invites or changes roles, and never to a role with permissions they lack. GET /v1/roles returns this table as the service enforces it.
Permissions
Underneath, a role is a set of permissions. A permission is read or write access to one area, written <area>:read or <area>:write. Most areas have both levels, but sla, audit and billing are read only, and incident_publishing, project_admin and ownership are write only. The areas are alerts, incidents, incident_publishing, mutes, oncall_overrides, oncall, projects, project_admin, sources, rules, maintenance, checks, views, webhooks, integrations, actions, reports, logs, secrets, sla, status_pages, incident_settings, team_settings, members, audit, billing and ownership. Each of the five roles above is a built-in preset: a fixed set of these permissions that grants exactly what the table describes. A viewer holds every :read. A responder adds alerts, incidents, mutes and oncall_overrides writes. A member adds the writes the table lists for members. An admin holds everything but ownership, which only the owner has.
Custom roles
An owner can define up to 20 custom roles per tenant. Each one is a name plus a chosen set of permissions. A write brings its area’s read with it: a role saved with mutes:write also holds mutes:read. A custom role is assigned wherever a built-in role is: as a member’s team role, as an invite’s role, or as the role of a per-project grant. Its id is cr_ followed by 16 lowercase hex characters, and a role field holds that id, never the name. The built-in roles keep owner, admin, member, responder and viewer as their ids.
- Only owners create, edit and delete custom roles.
- Any permission but
ownershipcan go in a custom role,billing:readincluded. Ownership stays with the built-in owner role. - Member management is capped at admin’s.
members:writeis the only permission that manages members, and it gives exactly an admin’s member powers. Its holder can grant only roles whose permissions they also hold, and can change, remove or revoke the credentials of only a member whose team role and every per-project grant’s role they could give. They can never grant owner or act on an owner. Invites and pairing codes are checked against their sender when redeemed, and a change to the sender’s role or grants cancels the ones still pending. - Through a per-project grant, only project-scoped permissions apply. For a project-limited member, team-wide routes such as billing, members and team settings still follow their team role.
- A role in use cannot be deleted, only replaced. Deleting it names a replacement, which can be any built-in role but owner, or another custom role. In one step, every member, per-project grant, pending invite and unused pairing code that holds the role moves to the replacement, and then the role is deleted.
Per-project roles
By default a member’s role applies to every project in the tenant. An owner, an admin or another holder of members:write can instead limit a member to chosen projects, with a role for each: a contractor who works on web as a member sees nothing of billing. Owners cannot be limited.
- Projects outside the grants do not exist for the member. Any route under
/v1/projects/{id}for a project they lack answers404{"error": "unknown project"}, the same body as a project that really does not exist. Lists that span projects (projects, alerts and the/v1/pullfeed, sources, logs and log tailing, secret findings, check and response-time SLA reports, integrations, incidents) show only granted projects, and a?project=filter for another project returns nothing. - Inside a granted project, the grant’s role decides. It can be any role but owner, above or below the member’s team role, and never with permissions the person granting it lacks.
- A list follows the grant roles, not the team role. Each of those lists needs its read permission, such as
logs:readfor logs, and shows only the projects whose grant role holds it. A member whose team role is a custom role withoutlogs:read, granted member onweb, seesweb’s logs. A member granted a custom role onwebthat holds onlychecks:readsees nothing ofwebin the logs, and if no grant holds the read, the list is a403, whatever the team role. - Routes that name an object, not a project, use the grant role of the object’s project: acknowledging or resolving an alert, reading or deleting a log line, and every
/v1/incidents/{number}route. An object in an ungranted project is a404:unknown alert,unknown log lineorunknown incident. - Team-wide things follow the team role. That covers status pages, component SLA reports, members, API keys, shared views, the relay and the audit log, though a limited member can revoke only their own credentials (below). What these show still leaves out ungranted projects. The audit log drops entries about an ungranted project and keeps team-level ones. The members list shows each member’s grants only on projects the caller also holds, so a member whose grants all lie elsewhere shows
[]. The relay lists only granted projects. A shared view whose filter names an ungranted project is hidden. Status-page components all show, but a binding to an ungranted project is left out, along with the timeline phases and component SLA uptime that come from it. - The on-call schedule list shows every schedule, because who is on call is team-wide. A limited member can read it only if one of their grant roles holds
oncall:read. Otherwise it is a403. - Incidents are visible to a limited member only if the incident’s own project, or the project of an alert attached to it, is granted. Any other incident is a
404{"error": "unknown incident"}and is left out of the list. On an incident they can see, the highest of their grant roles among those projects applies. Opening an incident must name a granted project, or the answer is404{"error": "unknown project"}. Attaching alerts to one needs every alert to be in a granted project, or the answer is404{"error": "unknown alert"}. To show which projects an incident touches, GET/v1/incidentsand/v1/incidents/{number}returnprojects: the incident’s own project plus the projects of its attached alerts, as a list of names. On an incident they can see, what is shown follows only the grants whose role holdsincidents:read: alerts andprojectsentries from other projects are left out of the list, GET/v1/incidents/{number}, the answer to attaching alerts and to every other incident write, the report at/v1/projects/{id}/reports/{rid}and each row of/v1/projects/{id}/reports, and an AI assistant’s incident read. Entries recording such an alert’s cause are left out of every incident and report answer that carries entries, and thetimelineof GET/v1/incidents/{number}also drops the items about such an alert: its opening, attaching and resolving, and recorded events about it. A member granted a custom role onwebthat holds onlychecks:readsees none ofweb’s alerts on an incident they reach through another project. Cause entries recorded before Pingtower began noting their project still show. In the list and on the incident,projectis""when the incident’s home project is outside those grants, andalert_idis left out when it names an alert there. - Some routes are for unrestricted members only. A limited member gets
403{"error": "not available to project-limited members"}when changing members or grants, when listing, creating or revoking invites, when creating a pairing code, when creating a project, and when changing issue trackers, the relay (PUT /v1/relay) or anything on a status page (the page, its logo, custom domain, components and phases), whatever their role. - A limited member revokes only their own credentials. They can always revoke their own API keys and AI connections, and their own box pairing if their role holds
members:write. Even withmembers:write, revoking another member’s API key or pairing is a403{"error": "not available to project-limited members"}, and another member’s AI connection is a404{"error": "unknown grant"}. GET/v1/tenants/{tid}/grantslists only their own AI connections, withcan_managefalse. - The member’s API keys and AI connections carry the same limits. They are read on every request, so a change to the grants applies at once. Their phones are not sent push notifications for alerts in other projects.
- Narrowing grants sends no deletions through
/v1/pull. A client that caches alerts should drop the ones outside theprojectsfrom GET/v1/rolesand pull again fromcursor=0when that list changes. - Deleting a project never widens access. A member whose only granted project is deleted keeps an empty list and sees no projects until an admin grants new ones. Renaming a project carries its grants over to the new name.
See Team & account for the routes.