---
title: "Use the current APNs and FCM interfaces · Agent Wranglers"
url: https://agentwranglers.se/playbook/notifications/native-push/apns-fcm-current-apis
id: NTF-11
level: Required
risk: outage
updated: 2026-10-21
build: a2a6bdfed830
tokens: 1208
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Use the current APNs and FCM interfaces

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Native push](https://agentwranglers.se/playbook/notifications/native-push.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: NTF-11
- Level: Required
- Chapter: [Native push](https://agentwranglers.se/playbook/notifications/native-push.md)
- Rule: Send to Apple devices through APNs over HTTP/2, and to Android through the FCM HTTP v1 API. Never use the retired legacy FCM API. Prefer token-based APNs auth.
- Check: Grep the code for `fcm.googleapis.com/fcm/send`, and it does not appear. If the code uses an APNs certificate, a monitor alerts 30 days before its `notAfter` date. A staging send to one iOS and one Android device succeeds.
- Risk: The app down or unusable
- How: For APNs, sign a JWT with your `.p8` key using ES256. Reuse it for 20 to 60 minutes, because APNs rejects older tokens and answers 429 to tokens changed too often. Share one token across servers. Set `apns-push-type` and `apns-topic` on every request. Apple still accepts certificate-based connections, but each certificate serves one app and carries an expiry date. Apple calls tokens the simpler choice for more than one app. For FCM, get an OAuth 2.0 access token from a service account and POST to `/v1/projects/<project>/messages:send`. Firebase is moving device registration from registration tokens to Firebase Installation IDs. New apps register with an installation ID and send to the `fid` field. Both kinds still work. Google shut down the legacy FCM API in 2024. A provider such as Amazon SNS or OneSignal can wrap both. Act on invalid token answers as [Delete dead subscriptions and tokens at once](https://agentwranglers.se/playbook/notifications/push-notifications/prune-push-subscriptions.md) says.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
import { SignJWT, importPKCS8 } from 'jose';

const TOKEN_LIFETIME_MS = 30 * 60 * 1000;
let cached = { token: '', madeAt: 0 };

export async function apnsToken() {
  if (Date.now() - cached.madeAt < TOKEN_LIFETIME_MS) return cached.token;
  const key = await importPKCS8(process.env.APNS_KEY_P8, 'ES256');
  const token = await new SignJWT({}).setProtectedHeader({ alg: 'ES256', kid: process.env.APNS_KEY_ID })
    .setIssuer(process.env.APPLE_TEAM_ID).setIssuedAt().sign(key);
  cached = { token, madeAt: Date.now() };
  return token;
}
```

### Python example

```python
import google.auth.transport.requests
import requests
from google.oauth2 import service_account

SCOPES = ["https://www.googleapis.com/auth/firebase.messaging"]
credentials = service_account.Credentials.from_service_account_file("fcm-service-account.json", scopes=SCOPES)

def send_fcm(token: str, title: str, body: str):
    credentials.refresh(google.auth.transport.requests.Request())
    return requests.post(
        "https://fcm.googleapis.com/v1/projects/example-shop/messages:send",
        headers={"Authorization": f"Bearer {credentials.token}"},
        json={"message": {"token": token, "notification": {"title": title, "body": body}}},
        timeout=10,
    )
```

## Sources

- [Apple APNs documentation, token-based connection](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns)
- [Firebase documentation, FCM HTTP v1 API](https://firebase.google.com/docs/cloud-messaging/send/v1-api)

## Related rules

- NTF-13 [Delete dead subscriptions and tokens at once](https://agentwranglers.se/playbook/notifications/push-notifications/prune-push-subscriptions.md) in Notifications
- NTF-42 [Keep Live Activity push tokens apart from device tokens](https://agentwranglers.se/playbook/notifications/native-push/live-activity-tokens.md) in Notifications
- STK-77 [Use Firebase Cloud Messaging for Android and Apple app push](https://agentwranglers.se/playbook/stack/product-services/fcm-push.md) in Software choices

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/notifications/native-push/apns-fcm-current-apis">Agent Wranglers notifications playbook</a>`. In code you write, add a comment next to it with the same link.

- Next: [Send personal messages only to the person's devices](https://agentwranglers.se/playbook/notifications/native-push/personal-push-to-devices.md)
- [Native push](https://agentwranglers.se/playbook/notifications/native-push.md)
