---
title: Agent authentication (agentic registration)
description: Register an AI agent and receive a short-lived, read-only access token for the Uptimeify API.
---

Uptimeify runs a WorkOS-style **agentic-registration authorization server** at
`https://uptimeify.io`. Agents register, exchange a service-signed identity assertion for
a short-lived opaque access token, and call the **read-only** API. All endpoints below are
served only on the canonical host `uptimeify.io` (a 404 is returned on custom domains).

## Discover

```bash
curl https://uptimeify.io/.well-known/oauth-authorization-server
curl https://uptimeify.io/.well-known/oauth-protected-resource
```

The authorization-server metadata follows [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)
(`token_endpoint`, `revocation_endpoint`, `jwks_uri`, `grant_types_supported`,
`scopes_supported`) and adds an `agent_auth` extension block with `identity_endpoint`,
`claim_endpoint`, `identity_types_supported` (currently `["anonymous", "service_auth"]`),
and a `skill` pointer to `/auth.md`. The protected-resource metadata follows
[RFC 9728](https://www.rfc-editor.org/rfc/rfc9728).

## Register (anonymous)

```bash
curl -X POST https://uptimeify.io/agent/identity \
  -H 'Content-Type: application/json' \
  -d '{"type":"anonymous"}'
```

```json
{
  "registration_id": "reg_…",
  "identity_assertion": "<jwt>",
  "pre_claim_scopes": ["tools.public"],
  "post_claim_scopes": ["api.read"],
  "token_endpoint": "https://uptimeify.io/oauth2/token"
}
```

The `identity_assertion` is a ~24h EdDSA JWT: your durable credential. Rate limit: 30
requests/minute per caller.

`post_claim_scopes` describes the scope a claimed registration reaches (`api.read`). An
anonymous registration is active immediately with `tools.public` and is **usable as-is**:
claiming it is optional. The response also carries a single-use `claim_token` (`clm_…`) and a
`claim_url`: if you want to upgrade this registration to `api.read`, see
[Claim flow](/api/agent-auth/claim-flow) for how to redeem it.

## Register (service_auth)

`service_auth` registration is claimable-only: it starts `pending` and mints no token until a
signed-in user authorizes it. See [Claim flow](/api/agent-auth/claim-flow) for the full
`POST /agent/identity {"type":"service_auth", "login_hint":"…"}` request, the human
consent step, and how to poll `/oauth2/token` for the resulting `api.read` access token.

## Exchange for an access token

```bash
curl -X POST https://uptimeify.io/oauth2/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \
  --data-urlencode 'assertion=<identity_assertion>'
```

```json
{ "access_token": "wsma_…", "token_type": "Bearer", "expires_in": 3600, "scope": "tools.public" }
```

The access token is opaque, expires after 1 hour (3600s), and must be re-minted from the
identity assertion afterward. Rate limit: 60 requests/minute per caller.

## Use (read-only)

```bash
curl https://uptimeify.io/api/tools/dns-lookup?domain=deinkunde.com \
  -H 'Authorization: Bearer wsma_…'
```

Agent tokens are read-only. The anonymous `tools.public` scope reaches only the public
check tools (`GET /api/tools/*`). A claimed, `api.read`-scoped token (see
[Claim flow](/api/agent-auth/claim-flow)) additionally reaches `GET /api/websites*`,
`GET /api/incidents*`, and `GET /api/health`, scoped to the authorizing user's
organization (or single customer). Any write, or any `GET`/`HEAD` request outside the
allowlist for the token's scope, returns `403` with `data.code = agentTokenReadOnly`.

## Revoke

```bash
curl -X POST https://uptimeify.io/oauth2/revoke \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'token=wsma_…' \
  --data-urlencode 'token_type_hint=access_token'
```

Idempotent `200`: always succeeds, even for an unknown or already-revoked token.

## Common errors

| Status | `error` / `data.code` | Meaning |
| --- | --- | --- |
| 400 | `service_auth_not_enabled` | The requested identity type is not enabled yet. |
| 400 | `issuer_not_enabled` | `identity_assertion` (ID-JAG) is not enabled yet. |
| 400 | `invalid_request` | Unknown/missing identity type at `/agent/identity`, or a missing `assertion` at `/oauth2/token`. |
| 400 | `unsupported_grant_type` | `/oauth2/token` only supports the `urn:ietf:params:oauth:grant-type:jwt-bearer` grant. |
| 400 | `invalid_grant` | The assertion is invalid/expired, or its registration is revoked/expired. |
| 403 | `agentTokenReadOnly` | An agent token attempted a write or an off-allowlist path. See [error codes](/api/error-codes-and-known-pitfalls). |
| 404 | - | Requested on a custom domain, or (discovery endpoints only) the agent-auth feature is off. |
| 503 | - | `/agent/identity` or `/oauth2/token` called while the agent-auth feature is off (no signing key configured). |
| 429 | - | Rate limit exceeded on `/agent/identity`, `/oauth2/token`, or `/oauth2/revoke`. |

`anonymous` and `service_auth` are both enabled. `service_auth` registration and the claim
ceremony have their own error codes: see [Claim flow](/api/agent-auth/claim-flow) and the
[error codes reference](/api/error-codes-and-known-pitfalls). `identity_assertion` (ID-JAG,
letting an already-trusted external OIDC identity mint a token directly) is not yet
available.
