Push notifications

Your users get notified when a long-running agent finishes, even after they close the app. The conversation stays on the Ably channel, and Ably Push delivers the alert to the device that originally subscribed.

Push notifications let you notify users when an agent completes a long-running task, even if the user has closed the app. Push notifications use Ably's native Push API alongside AI Transport.

Diagram showing a push notification reaching a device while the agent finishes a long-running task

The pattern

The flow:

  1. A user sends a message that triggers a long-running agent task (research, code generation, data analysis).
  2. The user closes the app or navigates away.
  3. The agent completes the task, and its response is on the conversation's channel.
  4. A push rule on the channel triggers a notification to the user's device.
  5. The user taps the notification and returns to the completed conversation.

The conversation's channel and your own store hold the full response, so it is available when the user returns.

Set up push rules

Configure a channel rule in the Ably dashboard to trigger push notifications when the agent publishes a completion event:

OpenAI
Vercel AI SDK
JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

// Agent code: publish a completion event when the agent finishes.
import * as Ably from 'ably';
import OpenAI from 'openai';
import { createTransport } from '@ably/ai-transport';
import { openai } from '@ably/ai-transport/openai';

const ably = new Ably.Realtime({ key: process.env.ABLY_API_KEY });
const client = new OpenAI();

export async function POST(req) {
  const { channelName, userId, items, requestId } = await req.json();
  const transport = createTransport({ client: ably, channelName, codec: openai });

  try {
    const stream = await client.responses.create(
      { model: 'gpt-5.5', input: [...(await loadConversation(channelName)), ...items], stream: true },
      { signal: req.signal },
    );
    await transport.pipe(stream, {
      signal: req.signal,
      headers: { requestId },
    });
  } finally {
    await transport.close();
  }

  // The pipe resolved, so the response is complete. Publish a push-eligible event on a notification channel.
  const notificationChannel = ably.channels.get(`notifications:${userId}`);
  await notificationChannel.publish('agent-complete', {
    title: 'Your agent has finished',
    body: 'Tap to view the response',
    channelName,
  });

  return new Response(null, { status: 204 });
}

On the client, register the device for push notifications and subscribe to the notification channel:

JavaScript

1

2

3

// Client: subscribe for push notifications
const notificationChannel = ably.channels.get(`notifications:${userId}`)
await notificationChannel.push.subscribeDevice()

Open the conversation from the notification

The notification payload can include the conversation's channel name, so the client opens directly to the right conversation. When the user taps the notification, load the conversation from your store and replay the channel from the stored serial to show the completed response.

Edge cases and unhappy paths

  • A pipe that is cancelled or fails rejects, so the route above does not reach the push publish. Publish the notification only after the pipe resolves.
  • A device that never registered for push (no notificationChannel.push.subscribeDevice() call) does not receive the alert. Register on first launch and after a token refresh.
  • Notification payload limits apply per platform (around 4KB for APNs). Do not embed the full agent response; carry the channel name and load the conversation when the user returns.
  • A user with several devices receives the notification on every registered device. Deduplicate at the device level if you only want one.
  • The notification channel is a separate Ably channel from the conversation channel. Capabilities scope independently; the user's token needs subscribe on both, plus push-subscribe on the notification channel.

FAQ

Do I need a separate notification channel?

Yes. The push rule is per-channel; using a dedicated notifications:{userId} channel keeps the push policy off the conversation channel and lets you scope capabilities cleanly.

Does AI Transport publish the notification for me?

No. The SDK has no built-in push integration. The agent decides when the response is done enough to notify and calls channel.publish on the notification channel; Ably's push rule turns that message into a device notification.

Can I reuse the conversation channel for push?

You can, but every streamed message would then trigger the push rule. Publish one completion event on a separate channel and the user gets one notification.

What happens if the user opens a new device after the response completes?

The notification fires only on devices that had registered for push before the agent published. A new device sees the completed conversation when it loads the conversation and replays the channel.

How do I avoid duplicate notifications across devices?

Either subscribe only one device per user, or include a messageId in the payload and dedupe on the client. Ably itself does not dedupe across a device fleet.