Server = intent, phone = fire. Any program — a Telegram bot, a cron job, a home-automation hook — can read a user's upcoming wakes and queue changes. Nothing here rings: every write becomes an intent the iPhone applies (or declines) on its next sync.
Open Pace → Settings → Connect an agent. The app shows an 8-character pairing code, valid 10 minutes, single use. Exchange it once:
curl -s https://mcp-production-e0af.up.railway.app/v1/tokens/exchange \
-H 'Content-Type: application/json' \
-d '{"pairing_code":"K7PM-3QXZ","name":"my telegram bot","scope":"wakes:read wakes:write"}'
# → {"token":"pat_…","name":"my telegram bot","scope":"wakes:read wakes:write","expires_at":"…","token_id":"tok_…"}
The token lives 90 days, is stored only as a hash, shows up on the phone as a connected agent, and can be revoked from the phone or with DELETE /v1/tokens/me.
| Call | Scope | Returns |
|---|---|---|
GET /v1/wakes/next | read | the next wake that will actually ring, or 404 no_upcoming_wake |
GET /v1/wakes?days=14 | read | every occurrence in the window (1–60 days), skipped ones included |
POST /v1/wakes/proposals | write | 202 intent: a new one-time wake on a date |
POST /v1/occurrences/{id}/move | write | 202 intent: new time for one day |
POST /v1/occurrences/{id}/skip | write | 202 intent: no ring that day |
POST /v1/occurrences/{id}/note | write | 202 intent: reason shown when it rings |
GET /v1/intents/{id} | read | what the phone did: pending → acked → applied | rejected, or expired |
Occurrence ids are <alarm_id>@<YYYY-MM-DD> and stable. All times are local to the user's zone (tz). Errors are {"error":"snake_code","message":"…"}. 60 requests per minute per token; 429 carries Retry-After. Send an Idempotency-Key header on POSTs to make retries safe — a replay returns the original 202 with "deduplicated": true.
Full reference below (rendered from openapi.json).