Developer docs
-- Any atproto app can ask users to receive notifications via {PROJECT_NAME}. Users approve in - the dashboard; the relay delivers via Telegram. -
-
- Two endpoints, two auth mechanisms.
- requestPermission proves
- the user authorized this request (user OAuth);
- send proves
- the sender identity (your app's own DID key).
-
- Prefer a working example? A complete, ~300-line app wiring up - both flows is live at - example.notify.atmo.tools - — try it, then read the - source ↗. -
-1. Get a DID for your app
-
- Needed for send. The simplest option is
- did:web:
-
-
-
- Host
/.well-known/did.jsonon your app's domain.
- -
- Generate a P-256 keypair and put the public key in the DID document as a
-
verificationMethodwhose id ends in -#atproto. -
- - - Reference: - atproto DID spec ↗ - -
2. Request permission (user OAuth)
-
- The user signs into your app via atproto OAuth. Add just the
- requestPermission method to your app's OAuth scope —
- send uses your app's own key, not the user's session,
- so it doesn't belong here:
-
- atproto rpc?lxm=tools.atmo.notifs.requestPermission&aud=* -
-
- Then mint a service-auth JWT on the user's PDS via
- com.atproto.server.getServiceAuth and call:
-
- Returns { id, status }
- (pending or
- alreadyGranted). The user approves in their dashboard or
- via Telegram. title ≤ 50 chars,
- description ≤ 200 chars, optional
- iconUrl.
-
3. Send a notification (your app's key)
-
- Once granted, sign with your app's own key (no user involved) and send. Field limits:
- title ≤ 100, body
- ≤ 500, optional uri and
- threadKey.
-
- Easiest with @atcute/client (pass the JWT per call):
-
…or any HTTP client:
- {@render codeblock(data.code.sendCurl)} -4. Rate limits
--
-
- At most 1 outstanding pending request per (sender, recipient). -
requestPermission: 50 / hour per recipient and 100 / hour per sender.
- send: 1 / second and 100 / day per (sender, recipient).
-
5. Error handling
-Common XRPC errors:
--
-
AuthenticationRequired— missing/invalid JWT.
- NotAuthorized— no active grant for this recipient.
- RateLimitExceeded— slow down (seeRetry-After).
- InvalidRequest— malformed body (e.g. badsenderDid).
-