agentsclimarketplace

Push notifications backend

Skill almasumdev/awesome-mobile-backend-agent-skills/.github/skills/push/push-notifications-backend

Send push to mobile via FCM HTTP v1 and APNs over HTTP/2 with JWT, handle payload limits and token invalidation. Use when building or operating the push delivery service.From its SKILL.md

Install
npx -y skills add almasumdev/awesome-mobile-backend-agent-skills --skill push-notifications-backend

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.3 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it

Push Notifications Backend

Instructions

FCM (Android, iOS via Firebase) and APNs (iOS direct) are the two mainstream providers. Design the backend so business services never call them directly; a dedicated notification-service owns all provider integration.

1. Architecture

 producer (any service) -> enqueue(event)
                                |
                                v
                       ┌─────────────────┐
                       │ push-scheduler  │  (dedup, targeting, throttling)
                       └─────────────────┘
                                |
                                v
                       ┌─────────────────┐
                       │   push-sender   │  (FCM/APNs clients, retries)
                       └─────────────────┘
                                |
                                v
                       ┌─────────────────┐
                       │ delivery-logger │  (metrics, token invalidation)
                       └─────────────────┘

Queue options: Redis Streams, SQS, Kafka. Use a durable queue -- push is at-least-once.

2. FCM HTTP v1

Deprecate the legacy server-key API. Use the v1 endpoint:

POST https://fcm.googleapis.com/v1/projects/{PROJECT_ID}/messages:send
Authorization: Bearer <oauth2-token-from-service-account>
// Node/TS
import { GoogleAuth } from "google-auth-library";

const auth = new GoogleAuth({ scopes: ["https://www.googleapis.com/auth/firebase.messaging"] });

async function sendFcm(token: string, notification: { title: string; body: string }, data: Record<string, string>) {
  const client = await auth.getClient();
  const url = `https://fcm.googleapis.com/v1/projects/${PROJECT_ID}/messages:send`;
  const res = await client.request({
    url,
    method: "POST",
    data: {
      message: {
        token,
        notification,
        data, // strings only
        android: { priority: "HIGH" },
        apns: { headers: { "apns-priority": "10" } },
      },
    },
  });
  return res.data;
}

Cache the OAuth2 access token for ~55 minutes; do not re-mint per request.

3. APNs with JWT

Use HTTP/2 with provider-token (JWT) authentication, not the certificate-based flow.

// Go with golang.org/x/net/http2
type APNs struct {
    KeyID      string
    TeamID     string
    PrivateKey *ecdsa.PrivateKey // ES256
    Client     *http.Client      // http2 configured
}

func (a *APNs) providerToken() string {
    claims := jwt.MapClaims{"iss": a.TeamID, "iat": time.Now().Unix()}
    tok := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
    tok.Header["kid"] = a.KeyID
    s, _ := tok.SignedString(a.PrivateKey)
    return s
}

func (a *APNs) Send(deviceToken string, payload []byte, topic string) error {
    req, _ := http.NewRequest("POST",
        "https://api.push.apple.com/3/device/"+deviceToken, bytes.NewReader(payload))
    req.Header.Set("authorization", "bearer "+a.cachedProviderToken())
    req.Header.Set("apns-topic", topic)
    req.Header.Set("apns-push-type", "alert")
    req.Header.Set("apns-priority", "10")
    resp, err := a.Client.Do(req)
    if err != nil { return err }
    defer resp.Body.Close()
    return handleApnsResponse(resp)
}

Provider tokens are valid for ~1 hour; rotate at ~50 minutes. Keep a single HTTP/2 connection warm per worker.

4. Payload Limits

  • APNs alert: 4 KB. VoIP: 5 KB.
  • FCM: 4 KB.

Design payloads under 3 KB to leave headroom. Long content lives on the server; the push carries an id the app fetches.

{
  "notification": { "title": "New message", "body": "Ada: see you at 5" },
  "data": { "type": "message", "thread_id": "thr_01HX7...", "message_id": "msg_01HX7..." }
}

5. Token Management

  • Client registers its push token at login and on every refresh (tokens rotate).
  • Store (user_id, device_id, provider, token, updated_at).
  • On send, handle provider errors:
    • FCM: UNREGISTERED, INVALID_ARGUMENT (bad token) -> purge.
    • APNs: 410 Unregistered, 400 BadDeviceToken -> purge.
  • On logout, mark token inactive immediately.

6. Retry and Reliability

  • Retry transient errors (5xx, network) with exponential backoff + jitter, cap at ~5 attempts.
  • Do not retry terminal errors (400, 403, 404, 410). Log and purge as needed.
  • Deduplicate by (user_id, event_id) with a TTL keyed in Redis so a retry of the producer does not double-send.

7. Prioritization

  • high / priority 10: user-visible, urgent.
  • normal / priority 5: user-visible, batched by OS.
  • Silent pushes: see silent-push skill. Keep them sparse; they share budgets.

8. Observability

Emit per send:

  • queue_latency_ms, send_latency_ms, provider_status, error_code, token_age_days.

Aggregate a delivery-success SLO per provider; alert when drops exceed a threshold for 10 minutes.

9. Client Consumption

iOS (Swift, AppDelegate):

func application(_ app: UIApplication,
                 didRegisterForRemoteNotificationsWithDeviceToken token: Data) {
  let hex = token.map { String(format: "%02x", $0) }.joined()
  Task { await api.registerPushToken(hex, platform: "apns") }
}

Android (Kotlin, FirebaseMessagingService):

class MyFms : FirebaseMessagingService() {
  override fun onNewToken(token: String) {
    CoroutineScope(Dispatchers.IO).launch { api.registerPushToken(token, platform = "fcm") }
  }
}

Checklist

  • Dedicated notification-service owns FCM / APNs; no direct calls from business services.
  • FCM uses HTTP v1 with cached OAuth2 tokens.
  • APNs uses HTTP/2 + provider JWT rotated every ~50 min.
  • Payloads ≤ 3 KB; large content fetched by id.
  • Token table stores (user_id, device_id, provider, token); purged on terminal errors.
  • Retries on transient errors only; exponential backoff with jitter.
  • Deduped by (user_id, event_id).
  • Delivery metrics and SLO dashboards in place.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 1 of the 12 instructions most data backend skills give in ~1.5k tokens

Counted across 229 of the 229 authors here whose files we hold, read 2026-08-07

  • Separate business logic into service layersin 22 of 229, across 15 files
  • Retry failures with exponential backoffhere, and in 21 of 229, across 14 files
  • Select only needed database columnsin 20 of 229, across 13 files
  • Abstract data access into repository classesin 19 of 229, across 12 files
  • Use centralized error handlersin 17 of 229, across 10 files
  • Use AsNoTracking for read-only queriesin 16 of 229, across 4 files
  • Use async/await for all I/O operationsin 16 of 229, across 5 files
  • Implement structured loggingin 15 of 229, across 4 files
  • Use dependency injection for all servicesin 14 of 229, across 2 files
  • Use resource-based URLs for REST APIsin 13 of 229, across 7 files
  • Invalidate cache after data changesin 13 of 229, across 9 files
  • Use a dependency injection containerin 12 of 229, across 4 files

Said here and by no other author read

  • use a dedicated notification-service for all providers
  • use a durable queue for delivery
  • use the FCM HTTP v1 endpoint
  • cache the OAuth2 access token for 55 minutes
  • use HTTP/2 with JWT authentication for APNs
  • rotate APNs provider tokens every 50 minutes

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 326,782. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.