Maintenance Due

Agent builders

Personal Agent Protocol on Maintenance Due

A personal agent can reach an owner's due board and service log through Personal Agent Protocol v0.1. The owner signs in on our page and picks view or change. Maintenance Due is free, and the due path does not call a model.

This page is for people who build personal agents. If you only want Muse to keep the van's oil on schedule, copy the prompt at /for-your-agent instead. The spec lives at personalagentprotocol.org.

Discovery

GET https://maintenancedue.com/.well-known/poppy.json names the organization (maintenancedue.com), the issuer https://maintenancedue.com, Direct Sign-In with poppy:read and poppy:write, and two APIs: the MCP server at https://maintenancedue.com/mcp and the OpenAPI description at https://maintenancedue.com/openapi.json for REST under /api/v1. The issuer metadata at /.well-known/oauth-authorization-server lists maintenancedue.com in poppy_domains and gives the token, authorization, and revocation endpoints under /oauth/.

Sessions and sign-in

  1. Publish your client. Your client_id is an https URL that serves your client metadata document, with jwks_uri and redirect_uris on the same domain. Every token request carries a private_key_jwt client assertion signed with a key from that jwks_uri.
  2. Start a Session. POST /oauth/token with grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer, an assertion whose iss is your client_id, sub is the User ID, and aud is https://maintenancedue.com/oauth/token as a single string, plus a DPoP proof. You get a signed-out Session Token (token_type DPoP, one hour) and a session_id. Send session_id with a fresh assertion to renew it.
  3. Send the owner to our page. Open /oauth/authorize with response_type=code, your client_id and redirect_uri, scope=poppy:read poppy:write (or only poppy:read), a state, and a PKCE S256 code_challenge. The owner sees your name and domain, picks View only or View and change, then creates a free shop or signs in with a membership token they already hold. The redirect brings back code, state, and iss. Check that iss is https://maintenancedue.com.
  4. Exchange the code. POST /oauth/token with grant_type=authorization_code, the code, redirect_uri, code_verifier, and your session_id, plus your client assertion and a DPoP proof. The response carries the Account Token in refresh_token (pat_…, 30 days), a signed-in Session Token, and the granted scope. The owner may pick view when you asked for change, so read scope.
  5. Start signed in later. POST /oauth/token with grant_type=refresh_token and the Account Token, with or without session_id. Add scope=poppy:read for a read-only token on a lookup. The Account Token is not rotated, so parallel Sessions can share it.
  6. Sign out. POST /oauth/revoke with the Account Token and your client assertion. Every Session signed in with it is signed out on its next call and renews signed out.

Calling the board

REST takes DPoP: Authorization: DPoP <token> plus a fresh DPoP proof with htm, htu, and ath on every call to /api/v1. MCP takes Bearer: renew without a DPoP proof and with resource=https://maintenancedue.com/mcp, then send Authorization: Bearer <token> to /mcp. A Bearer Session Token works only on /mcp, and a DPoP token is refused there. The MCP tools, their schemas, and the MCP-Protocol-Version 2025-06-18 header are the same ones ChatGPT uses.

View or change

poppy:read opens every GET on /api/v1 and every MCP tool marked readOnlyHint, such as get_due, get_history, and list_assets. poppy:write opens every other REST method and MCP tool, such as append_event, append_reading, create_service, and set_rule, plus events/subscribe. A missing scope is 403 with error="insufficient_scope" and the scope it needs in WWW-Authenticate. A signed-out token on an account call is 403 sign_in_required. An expired token, or one issued before sign-out, is 401 invalid_token. Inviting, removing, or leaving org members stays with the member's own token.

ChatGPT connections made through /oauth/authorize with the mcp scope, and existing Muse membership tokens, keep working as they did. Field names live on /skill.md, the REST contract on /openapi.json, and the agent index on /llms.txt.