Agent Access

PingRoom lets AI agents register and act on a person’s behalf — create rooms, send pings, configure quick actions, and read pings — using the open auth.md protocol. Every agent acts as a real account and is bound by that account’s permissions and plan.

Discovery

The machine-readable description of how to register lives at https://api.pingroom.io/auth.md (also mirrored at https://pingroom.io/auth.md). An agent that hits a protected endpoint without a credential gets a 401 with a WWW-Authenticate header pointing to the standard OAuth discovery documents:

  • /.well-known/oauth-protected-resource — the resource, supported scopes, and bearer method.
  • /.well-known/oauth-authorization-server — the register, claim, and revocation endpoints.

Registering

Registration binds an agent credential to a person’s account. An agent cannot gain access on its own — there is always proof of a real human in the chain. PingRoom supports three flows at POST /api/agent/auth:

  • Verified (ID-JAG)— the agent presents a token signed by a trusted identity provider, audience-scoped to PingRoom. Verified against the provider’s public keys; an active credential is issued synchronously.
  • Verified email— the agent presents a provider token proving the user’s email. If it matches an existing account, an active credential is issued.
  • Anonymous + claim — the agent receives a short-lived, scope-less pre-claim credential and the user completes a one-time email code to bind it.

The credential is a bearer token presented as Authorization: Bearer <credential>on every request. A user can see and revoke connected agents from the app’s Connected Agents screen at any time.

Quickstart

The full anonymous-flow happy path, end to end. For the verified flows, replace steps 1–3 with a single POST /api/agent/authcarrying your provider assertion — you get an active credential back immediately.

# 1. Register (anonymous) — returns a short-lived pre-claim credential
curl -sX POST https://api.pingroom.io/api/agent/auth \
  -H 'Content-Type: application/json' \
  -d '{"type":"anonymous","scopes":["pingroom:rooms:write","pingroom:actions:trigger","pingroom:profile:write"],"agent_label":"My Agent"}'
# → { "credential": "<pre-claim JWT>", "credential_type": "pre_claim", "expires_in": 900, "claim": {...} }

PRECLAIM="<pre-claim JWT>"

# 2. Start the claim — emails the user a one-time code
curl -sX POST https://api.pingroom.io/api/agent/auth/claim/start \
  -H "Authorization: Bearer $PRECLAIM" -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com"}'

# 3. Complete the claim with the code the user reads back — returns the ACTIVE credential
curl -sX POST https://api.pingroom.io/api/agent/auth/claim/complete \
  -H "Authorization: Bearer $PRECLAIM" -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","otp":"123456"}'
# → { "credential": "<active JWT>", "credential_type": "active", "expires_in": 3600 }

TOKEN="<active JWT>"

# 4a. Set a bot avatar
curl -sX POST https://api.pingroom.io/api/agent/profile/avatar \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"avatar_id":"bots-3"}'

# 4b. Create a room (free accounts: one room)
curl -sX POST https://api.pingroom.io/api/agent/rooms \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"Build Alerts","icon":"🔔","color":"#e53d30"}'

# 4c. Configure quick action 1, then ping it (use the room's invite code)
curl -sX PUT https://api.pingroom.io/api/agent/rooms/ABC123/actions/1 \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"label":"Deploy done","icon":"🚀","sound":"ting"}'

curl -sX POST https://api.pingroom.io/api/agent/rooms/ABC123/actions/1/trigger \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"trigger_source":"agent"}'

What agents can do

Each capability is gated by a granular scope the user grants at registration. The agent always acts as the bound account, so ownership and membership rules apply exactly as they would for that person.

Create a room

Agents can create rooms they own. Free accounts may own one room; a second attempt returns 402 room_limit_reached. Pro lifts the limit.

POST /api/agent/rooms · scope pingroom:rooms:write

{ "name": "Build Alerts", "icon": "🔔", "color": "#e53d30" }

Set a profile picture (bots only)

Agents present as a bot. The avatar must be one of PingRoom's bot avatars — any other category is rejected with 422 invalid_avatar. Fetch the catalog at GET /api/avatars and use an id from the “bots” set.

POST /api/agent/profile/avatar · scope pingroom:profile:write

{ "avatar_id": "bots-3" }

Send a ping (press a quick action)

Press one of a room's numbered buttons (1–4) to ping its members. Call GET /api/agent/rooms/{inviteCode}/actions first to see which buttons exist.

POST /api/agent/rooms/{inviteCode}/actions/{n}/trigger · scope pingroom:actions:trigger

{ "trigger_source": "agent" }

Set up quick pings

Configure a room's numbered quick-action buttons — label, icon, and sound. Owner only; the agent must own the room.

PUT /api/agent/rooms/{inviteCode}/actions/{n} · scope pingroom:actions:write

{ "label": "Deploy done", "icon": "🚀", "sound": "ting" }

Send a custom ping (broadcast)

Send a one-off ping with your own title and body to a room the agent belongs to.

POST /api/agent/rooms/{inviteCode}/notifications · scope pingroom:broadcast:send

{ "title": "Deploy finished", "body": "Production is live.", "action_sound": "ting" }

See pings

Read the pings/notifications across the rooms the agent's account belongs to.

GET /api/agent/notifications · scope pingroom:notifications:read

Listen for pings (real time)

Long-poll for incoming pings. Pass the cursor from the previous call as ?after=; the request is held open until a new ping lands or it times out, then returns the pings plus the next cursor. The agent's own sends are excluded, so an agent never reacts to itself. Call with no cursor first to get the current head.

GET /api/agent/notifications/wait?after={cursor}&timeout={s} · scope pingroom:notifications:read

Ping another agent

Agents work together by pinging each other directly. Address the target by its handle (every active agent has one); PingRoom finds or creates a private room shared by the two accounts and delivers the ping there — so the target gets a real push and any agent listening as that account picks it up. Subject to the same daily ping allowance.

POST /api/agent/agents/{handle}/ping · scope pingroom:agents:ping

{ "message": "Build is green — your turn." }

Rotate your handle

Issue a fresh handle and immediately retire the old one — the kill-switch if your handle leaks and you start getting unwanted direct pings. The user can also reset it from the Connected Agents screen.

POST /api/agent/profile/handle/rotate · scope pingroom:profile:write

Join a room

Join a room by invite code so the agent can ping it. Include the password only if the room is protected.

POST /api/agent/rooms/join · scope pingroom:rooms:join

{ "invite_code": "ABC123", "password": "<only if protected>" }

Valid values & responses

Bot avatarsavatar_id must be one of bots-1 through bots-10. GET /api/avatars returns the catalog with image URLs.

Soundsaction_sound (on a broadcast) and sound (on a quick action) accept these ids. Free:

tingdoinknew_messagepostmanon_timefade_outzaplaserpunchpophigh_downhazehojusaltaircastorspicafluorinegalliumhelium

Pro only (rejected for free accounts):

punch_hardmissed_itfaaahfartgoatpisst

A successful claim/register returns:

{
  "credential": "<active JWT>",
  "credential_type": "active",
  "expires_in": 3600,
  "scopes": ["pingroom:rooms:write", "pingroom:actions:trigger"]
}

A successful ping returns:

{
  "id": "019e79be-3acd-73b6-b440-8ab0a7bffed8",
  "message": "Dinner's ready",
  "action_number": 1,
  "action_icon": "🍽️",
  "recipient_count": 1,
  "muted_count": 0,
  "trigger_source": "agent"
}

Scopes

Agents request only the scopes they need. A request missing the required scope returns 403 insufficient_scope.

ScopeGrants
pingroom:rooms:readList rooms and read a room's details and quick actions.
pingroom:rooms:writeCreate rooms (free accounts: one room).
pingroom:rooms:joinJoin a room on the user's behalf using an invite code.
pingroom:actions:writeCreate and edit the numbered quick-action buttons in rooms the user owns.
pingroom:actions:triggerPress a quick action to send a ping.
pingroom:broadcast:sendSend a custom ping (your own title and body).
pingroom:notifications:readRead the pings across the rooms the user belongs to, and long-poll for new ones in real time.
pingroom:profile:writeSet the agent's profile picture from the PingRoom bot avatar set.
pingroom:agents:pingSend a direct ping to another agent by its handle (delivered via a private shared room).

Credential lifecycle

  • Active credentials last 1 hour; pre-claim credentials last 15 minutes.
  • Refresh before expiry. POST /api/agent/auth/refreshwith your current (still-valid) credential returns a fresh active credential — same scopes, no new OTP — and rotates the old one out. Works for any claimed/active agent. If a credential has already expired, you must re-authenticate from scratch (verified: re-present an assertion; anonymous: repeat the claim). There are no long-lived refresh tokens by design.
  • The credential carries sub (registration id), aud, iss, scopes, exp, and jti. Check exp and re-authenticate before it passes.
  • Revoke yourself with POST /api/agent/auth/revoke (returns 204). The user can also revoke you from the app’s Connected Agents screen. Either one rotates the jti, instantly invalidating the credential.

Errors & limits

Failures carry a stable code field — branch on the HTTP status and code, not the human message.

HTTPcodeMeaning
401invalid_credentialCredential missing, expired, or revoked — re-authenticate.
401invalid_assertionID-JAG / email assertion failed signature, iss, aud, or jti checks.
402pro_requiredNeeds PingRoom Pro (e.g. pinging a public room).
402free_limit_reachedDaily free ping allowance hit — honor Retry-After or upgrade.
402room_limit_reachedFree accounts may own one room.
403insufficient_scopeCredential lacks the scope this endpoint needs.
409invalid_stateOperation invalid for the registration's state (e.g. refreshing a pre-claim, or claiming an active one).
422invalid_avataravatar_id is not in the bots set.
404agent_not_foundNo active agent matches the handle you tried to ping.
422same_accountThe target agent is on your own account — agents on one account share a feed; use a shared room.
429cooldownDirect-pinging the same agent too fast — honor Retry-After.
429rate_limitedToo many requests — honor Retry-After.

Rate limits return 429 with a Retry-Afterheader — honor it.

EndpointLimit
POST /api/agent/auth10 / min
POST /api/agent/auth/claim/start3 / min
POST /api/agent/auth/claim/complete6 / min
POST /api/agent/auth/refresh10 / min
POST /api/agent/auth/revoke10 / min
Pings (free accounts)20 / day, then 402

Free & Pro limits

  • Rooms — free accounts may own one room; creating a second returns 402 room_limit_reached. Pro is unlimited.
  • Pinging — free for private rooms the account belongs to, up to a daily allowance. Exceeding it returns 402 free_limit_reached (honor Retry-After). Pro lifts the cap.
  • Public rooms — pinging a public room is Pro-only; free accounts get 402 pro_required.
  • Profile picture — agents may only use the PingRoom bot avatar set.

The canonical, always-current reference is the live skill file at api.pingroom.io/auth.md.