Send push notifications using the Firebase API for AI video
Send a push with the FCM HTTP v1 API from your server when the video job's webhook arrives: POST messages:send with a short-lived OAuth 2.0 token.

To send a push notification using the Firebase API, your server POSTs a message to the FCM HTTP v1 endpoint, https://fcm.googleapis.com/v1/projects/<project-id>/messages:send, with a short-lived OAuth 2.0 access token from a service account and the device's registration token as the target. For a “your video is ready” push, send it from the webhook handler that hears the video job finish.
The Firebase facts below come from its HTTP v1, server environment and Admin SDK pages, read 2026-09-28. The video job side uses Sume's webhooks docs. Sume has no Firebase connector; both halves are plain HTTPS from your server.
How do I send a push notification with FCM HTTP v1?
Firebase's steps, in order:
- Get credentials from a service account in your Firebase project. On Cloud Functions and other Google Cloud runtimes, Firebase says to use Application Default Credentials.
- Mint a short-lived OAuth 2.0 access token for the scope
https://www.googleapis.com/auth/firebase.messaging, and send it asAuthorization: Bearer <access_token>. - POST a JSON body with a
messageholding the targettokenand anotificationwithtitleandbody. Adatapayload of your own fields can go with it or instead of it. - On success, the response is a JSON object with the message
name.
Should I use the Admin SDK or the HTTP v1 API?
Firebase calls the Admin SDK "the recommended method", with SDKs for Node, Java, Python, C# and Go, because it handles authentication for you; the HTTP v1 API is for developers "who prefer a raw server protocol" (server environment). The Admin SDK page now recommends addressing a device by its Firebase Installation ID (fid) and marks the token field deprecated, though "during the migration period, the token field also accepts FIDs" (Admin SDK). Either way, the ID comes from the client FCM SDKs in your React Native, Flutter or native app, and your server stores it per user. Firebase says registration tokens "must be kept secret".
When should the server send the push?
When the video job ends, not on a timer. A video job runs for a while, so the app shouldn't sit open polling. The server learns first and tells the device.
| Step | Where | What happens |
|---|---|---|
| 1 | App → your backend | Registers the device's FCM ID for the signed-in user |
| 2 | Backend → Sume | Submits the video job with a callback_url and stores job_id → user |
| 3 | Sume → backend | POSTs job.completed, job.failed or job.canceled once the job is terminal |
| 4 | Backend → FCM | Verifies the signature, looks up the user, POSTs to messages:send |
| 5 | App | Opens on tap and asks your backend for the finished file |
import { verifyWebhook } from "@sume-com/sdk";
export async function POST(req: Request) {
const raw = await req.text(); // raw body, before JSON.parse
const ok = await verifyWebhook({
body: raw,
headers: req.headers,
secret: process.env.SUME_COM_WEBHOOK_SIGNING_SECRET!,
});
if (!ok) return new Response("bad signature", { status: 401 });
const event = JSON.parse(raw);
const job = await claimJobOnce(event.job_id); // your DB; null on a repeat
if (job && event.event === "job.completed") {
await fetch(`https://fcm.googleapis.com/v1/projects/${PROJECT_ID}/messages:send`, {
method: "POST",
headers: { Authorization: `Bearer ${await getAccessToken()}`, "Content-Type": "application/json" },
body: JSON.stringify({ message: {
token: job.deviceToken,
notification: { title: "Your video is ready", body: "Tap to watch it." },
data: { job_id: event.job_id },
} }),
});
}
return new Response(null, { status: 204 });
}Is the webhook safe to act on?
Only after it verifies. verifyWebhook from Sume's SDK checks the sume-v1 HMAC over the raw body and the timestamp window (Verifying webhooks), and in current code it returns false when the secret is empty. Sume makes up to 10 delivery attempts and asks you to "use job_id as the idempotency key", so claimJobOnce should mark the job so a repeat sends no second push. Answer with a 2xx quickly: each attempt times out after 10 seconds, so move the FCM call to a queue if it may be slow (Webhooks). getAccessToken is the token helper from Firebase's own sample.
Should the push carry the video URL?
Better not. Send the job_id in data and let the app fetch the file URL from your backend after the user is signed in. The finished file lives at a public media.sume.com URL, and anyone who has that URL can fetch it, so it doesn't belong on a lock screen.
What if the webhook or the push never arrives?
Plan for both. Ten refused Sume deliveries leave a failed delivery and a job that still finished; the docs call delivery "an optimization, never the only recovery path", so keep polling the job as a backup (Webhooks). On the Firebase side, your server must be "able to handle requests and resend them using exponential back-off" (server environment). When the app opens, have it ask your backend for the user's latest jobs rather than trust that every push landed. Progress updates for long video jobs covers what to show while it runs.
Sources
Related posts
More in Integrations
- Firebase scheduled functions: call a paid API once a day
Declare a Firebase scheduled function with onSchedule, a cron and a timeZone, keep the API key in secrets, and key each paid call to scheduleTime.
- Google Sheets onEdit trigger: call an API when a row changes
A simple onEdit trigger can't call UrlFetchApp, which needs authorization. Use an installable edit trigger to call an API from a Sheets edit.
- Hermes Agent MCP server: add Sume in config.yaml
Add Sume's hosted MCP server to Hermes Agent under mcp_servers in config.yaml, with an API-key header or OAuth, and paid tools behind approval.
- HeyGen MCP server: URL, setup for Claude, and credits
HeyGen MCP runs at mcp.heygen.com/mcp/v1/. Add it to Claude Code or Claude, sign in with OAuth, and videos use your HeyGen plan's credits.
Written by Sume