Agent Auth · MachineMail Docs

JSON Web Key Set

These endpoints implement the auth.md agent onboarding protocol. They use OAuth-style error bodies ({"error": "...", "error_description": "..."}) instead of the uniform MachineMail error envelope.

Method Path Summary
GET /.well-known/jwks.json JSON Web Key Set
GET /.well-known/oauth-authorization-server OAuth Authorization Server Metadata
GET /.well-known/oauth-protected-resource OAuth Protected Resource Metadata
GET /.well-known/oauth-protected-resource/{path} OAuth Protected Resource Metadata (Path-Suffixed)
POST /agent/event/notify Receive Security Event Token
POST /agent/identity Register Agent Identity
POST /agent/identity/claim Start Claim Ceremony
GET /auth.md Agent Onboarding Document
GET /oauth2/authorize Authorization Endpoint
POST /oauth2/register Register OAuth Client
POST /oauth2/revoke Revoke Token
POST /oauth2/token Token Endpoint

JSON Web Key Set

http

GET /.well-known/jwks.json

The service’s public signing key (RFC 7517). Verifies the identity assertions and access tokens this service issues.

Authentication: None.

Response

200 JWKS (application/json)

Headers:

Field Description
Cache-Control string
keys Record<string, unknown>[] Required

Errors

Status Meaning
500 Unexpected server error.

Example

Request

curl "https://api.machinemail.dev/.well-known/jwks.json"

Response 200

{
  "keys": [
    {
      "key": "value"
    }
  ]
}

OAuth Authorization Server Metadata

http

GET /.well-known/oauth-authorization-server

RFC 8414 plus the auth.md agent_auth block. identity_types_supported lists the registration types MachineMail accepts.

Authentication: None.

Response

200 The metadata (application/json)

Field Description
resource Required
authorization_servers Required
scopes_supported Required
bearer_methods_supported Required
issuer Required
authorization_endpoint Required
token_endpoint Required
registration_endpoint Required
revocation_endpoint Required
device_authorization_endpoint Required
jwks_uri Required
service_documentation Required
grant_types_supported Required
response_types_supported Required
response_modes_supported Required
code_challenge_methods_supported Required
token_endpoint_auth_methods_supported Required
revocation_endpoint_auth_methods_supported Required
client_id_metadata_document_supported Required
authorization_response_iss_parameter_supported Required
agent_auth Required

Errors

Status Meaning
500 Unexpected server error.

Example

Request

curl "https://api.machinemail.dev/.well-known/oauth-authorization-server"

Response 200

{
  "resource": "string",
  "authorization_servers": ["string"],
  "scopes_supported": ["inboxes.read"],
  "bearer_methods_supported": ["string"],
  "issuer": "string",
  "authorization_endpoint": "string",
  "token_endpoint": "string",
  "registration_endpoint": "string",
  "revocation_endpoint": "string",
  "device_authorization_endpoint": "string",
  "jwks_uri": "https://example.com",
  "service_documentation": "string",
  "grant_types_supported": ["string"],
  "response_types_supported": ["string"],
  "response_modes_supported": ["string"],
  "code_challenge_methods_supported": ["string"],
  "token_endpoint_auth_methods_supported": ["string"],
  "revocation_endpoint_auth_methods_supported": ["string"],
  "client_id_metadata_document_supported": false,
  "authorization_response_iss_parameter_supported": false,
  "agent_auth": {
    "skill": "string",
    "identity_endpoint": "string",
    "claim_endpoint": "string",
    "events_endpoint": "string",
    "identity_types_supported": ["anonymous"],
    "identity_assertion": {
      "assertion_types_supported": ["string"]
    },
    "events_supported": ["string"]
  }
}

OAuth Protected Resource Metadata

http

GET /.well-known/oauth-protected-resource

RFC 9728. Referenced by the WWW-Authenticate header on 401 responses from /v0 and /mcp.

Authentication: None.

Response

200 The metadata (application/json)

Field Description
resource Required
authorization_servers Required
scopes_supported Required
bearer_methods_supported Required
resource_name Required
resource_logo_uri Required
resource_documentation Required

Errors

Status Meaning
500 Unexpected server error.

Example

Request

curl "https://api.machinemail.dev/.well-known/oauth-protected-resource"

Response 200

{
  "resource": "string",
  "authorization_servers": ["string"],
  "scopes_supported": ["inboxes.read"],
  "bearer_methods_supported": ["string"],
  "resource_name": "string",
  "resource_logo_uri": "https://example.com",
  "resource_documentation": "string"
}

Receive Security Event Token

http

POST /agent/event/notify

RFC 8935 push delivery from a trusted agent provider. The body is a signed SET (typ: secevent+jwt) with aud equal to the issuer, a unique jti, and an events claim containing https://schemas.workos.com/events/agent/auth/identity/assertion/revoked. Every registration for that (iss, sub) is revoked.

Authentication: None.

Request body

Content type: application/secevent+jwt. Required.

Type: string

Response

202 Accepted (empty body)

Errors

Status Meaning
400 SET rejected.
500 Unexpected server error.

Example

Request

curl -X POST "https://api.machinemail.dev/agent/event/notify" \
  -H "Content-Type: application/secevent+jwt" \
  --data-binary @body

Register Agent Identity

http

POST /agent/identity

Dispatches on type (auth.md). No API key is ever returned.

anonymous returns a pre-claim identity_assertion and the sandbox inbox (one ephemeral inbox on the shared domain); exchange the assertion at /oauth2/token for an access token.

service_auth withholds the assertion and returns a claim block until the named user (login_hint) confirms.

identity_assertion verifies an ID-JAG from a trusted provider. When the verified email belongs to an existing user it answers 401 interaction_required with a claim_token and claim block; when auth_time is too old, 401 login_required with max_age. Both set WWW-Authenticate: AgentAuth error="...".

Registration is rate limited per requester and overall: 429 rate_limited with Retry-After. anonymous and service_auth share a cap per requester of at most 1 per rolling hour and 3 per rolling 24 hours, and a platform-wide cap of about 100 per rolling hour and 1000 per rolling 24 hours.

Authentication: None.

Request body

Content type: application/json. Required.

Field Description
type Required
name Agent label (first 100 characters). Untrusted; shown as agent-described in the dashboard.
client_name Alias of name.
login_hint service_auth: email address of the user. Required for that type.
assertion_type identity_assertion: required for that type.
assertion identity_assertion: required for that type.

Response

200 Registered (application/json)

Headers:

Field Description
Cache-Control string
registration_id Required
agent_id Required
registration_type Required
identity_assertion Service-signed JWT to exchange at /oauth2/token.
assertion_expires string (date-time)
scopes The granted scopes.
claim_url Where to start the claim ceremony
claim_token Secret for /agent/identity/claim
claim_token_expires string (date-time)
claim service_auth registrations: the ceremony is already started.
inbox anonymous registrations: the ephemeral sandbox inbox.

Errors

Status Meaning
400 Invalid request.
401 OAuth error interaction_required or login_required.
429 OAuth error rate_limited.
500 Unexpected server error.

Example

Request

curl -X POST "https://api.machinemail.dev/agent/identity" \
  -H "Content-Type: application/json" \
  -d '{"type": "anonymous", "name": "Production"}'

Response 200

{
  "registration_id": "string",
  "agent_id": "string",
  "registration_type": "anonymous",
  "identity_assertion": "string",
  "assertion_expires": "2026-10-01T12:00:00.000Z",
  "scopes": ["inboxes.read"],
  "claim_url": "https://example.com",
  "claim_token": "string",
  "claim_token_expires": "2026-10-01T12:00:00.000Z",
  "inbox": {
    "pod_id": "pod_01k6x2m9qd0001b8j4mr0w3d5f",
    "email": "agent@machinemail.dev",
    "display_name": "Support Agent"
  }
}

Start Claim Ceremony

http

POST /agent/identity/claim

Called by the agent. Returns a 6-digit user_code and a verification_uri to show the human, who completes the claim in the dashboard.

Authentication: None.

Request body

Content type: application/json. Required.

Field Description
claim_token Required
email Binds the claim to this human.
agent_id Alternative to claim_token.

Response

200 Claim attempt started (application/json)

Field Description
registration_id Required
agent_id Required
claim_attempt_id Required
status Required
expires_at string (date-time)
claim_attempt Required
user_code Required
verification_uri Required
expires_in Required
interval Required

Errors

Status Meaning
400 Invalid request.
401 Invalid claim_token.
404 Unknown or revoked registration.
409 Already claimed.
410 Claim expired.
500 Unexpected server error.

Example

Request

curl -X POST "https://api.machinemail.dev/agent/identity/claim" \
  -H "Content-Type: application/json" \
  -d '{"email": "agent@machinemail.dev"}'

Response 200

{
  "registration_id": "string",
  "agent_id": "string",
  "claim_attempt_id": "string",
  "status": "initiated",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "claim_attempt": {
    "user_code": "string",
    "verification_uri": "https://example.com",
    "expires_in": 1,
    "interval": 1
  }
}

Agent Onboarding Document

http

GET /auth.md

Markdown instructions for agents (auth.md protocol).

Authentication: None.

Response

200 auth.md (text/markdown)

Field Description
Cache-Control string

Errors

Status Meaning
500 Unexpected server error.

Example

Request

curl "https://api.machinemail.dev/auth.md"

Authorization Endpoint

http

GET /oauth2/authorize

OAuth 2.1 authorization code grant.

Authentication: None.

Query parameters

Field Description
response_type Must be code.
client_id From registration.
redirect_uri One of the client’s redirect URIs.
code_challenge PKCE (RFC 7636). Required.
scope Space-separated Scope values. Default: every scope the client may ask for.
state Returned unchanged with the response.
resource Must be the MCP server’s URL.

Response

302 To the consent page, to redirect_uri with an error, or to the error page.

Errors

Status Meaning
400 Invalid request.
500 Unexpected server error.

Example

Request

curl "https://api.machinemail.dev/oauth2/authorize"

Register OAuth Client

http

POST /oauth2/register

RFC 7591 Dynamic Client Registration for apps and MCP clients.

Authentication: None.

Request body

Content type: application/json. Required.

Field Description
redirect_uris Where the authorization response may be sent.
client_name Shown on the consent page.
grant_types Must include authorization_code.
response_types Must include code.
scope Values the client may ask for. Default: every scope.
application_type Should use native or web.

Response

201 Registered (application/json) with headers:

Field Description
client_id The new client identifier.
client_id_issued_at Seconds since the epoch.
client_secret Confidential clients only.

Errors

Status Meaning
400 Invalid request.
429 Rate limited.
500 Unexpected server error.

Example

Request

curl -X POST "https://api.machinemail.dev/oauth2/register" \
  -H "Content-Type: application/json" \
  -d '{"redirect_uris": ["string"]}'

Response 201

{
  "client_id": "my-client-id",
  "client_name": "string",
  "redirect_uris": ["string"],
  "grant_types": ["string"],
  "response_types": ["string"]
}

Revoke Token

http

POST /oauth2/revoke

RFC 7009. Idempotent.

Authentication: None.

Request body

Content type: application/x-www-form-urlencoded or application/json. Required.

Field Description
token An access token, a refresh token or an identity assertion.
token_type_hint Type of token.
client_id Applicable for refresh and access tokens.
client_secret Required for a confidential client.

Response

200 Revoked (empty body)

Errors

Status Meaning
400 Invalid request.
401 Invalid client.
500 Unexpected server error.

Example

Request

curl -X POST "https://api.machinemail.dev/oauth2/revoke" \
  --data-urlencode "token=string"

Token Endpoint

http

POST /oauth2/token

The token endpoint of the MachineMail authorization server.

Authentication: None.

Request body

Content type: application/x-www-form-urlencoded or application/json. Required.

Field Description
grant_type Required
code Optional for authorization_code grant type.
refresh_token Required for refresh_token grant type.
claim_token Optional for claim grant type.
client_id Optional for authorization_code or refresh_token.
client_secret Required for confidential clients.
resource Optional audience pin.

Response

200 Access token (application/json)

Field Description
access_token The access token returned.
expires_in The expiration time of the token.
refresh_token The refresh token.
identity_assertion Identity assertion if applicable.

Errors

Status Meaning
400 Invalid request.
401 Invalid client.
500 Unexpected server error.

Example

Request

curl -X POST "https://api.machinemail.dev/oauth2/token" \
  --data-urlencode "grant_type=string"

Response 200

{
  "access_token": "string",
  "expires_in": 3600,
  "refresh_token": "string"
}