# Send Notifications

Source: https://pushary.com/docs/web-push/server-sdk/notifications
Last updated: 2026-05-11

> Send push notifications directly to subscribers

## Overview [#overview]

The `notifications.send()` method lets you send notifications directly to specific subscribers without creating a campaign. Ideal for transactional notifications like order updates, messages, and alerts.

## Basic Usage [#basic-usage]

```typescript
const result = await pushary.notifications.send({
  title: 'Order Shipped!',
  body: 'Your order #12345 is on its way.',
  url: 'https://example.com/orders/12345',
  subscriberIds: ['sub_123'],
})

console.log(result.sent)  // 1
```

## Send Options [#send-options]

```typescript
interface SendNotification {
  title: string           // Required: Notification title
  body: string            // Required: Notification body
  iconUrl?: string        // Notification icon
  imageUrl?: string       // Large image
  url?: string            // Click destination
  subscriberIds?: string[] // Target by subscriber IDs
  externalIds?: string[]  // Target by external IDs
  tags?: string[]         // Target by tags
  data?: object           // Custom data payload
}
```

## Targeting Options [#targeting-options]

### By Subscriber IDs [#by-subscriber-ids]

Send to specific subscriber IDs:

```typescript
await pushary.notifications.send({
  title: 'Direct Message',
  body: 'You have a new message',
  subscriberIds: ['sub_123', 'sub_456', 'sub_789'],
})
```

### By External IDs [#by-external-ids]

Send to your internal user IDs:

```typescript
await pushary.notifications.send({
  title: 'Account Alert',
  body: 'New login detected on your account',
  externalIds: ['user-123'],
})
```

External IDs are set via `pushary.setExternalId()` in the client SDK or `subscribers.update()` in the server SDK.

### By Tags [#by-tags]

Send to subscribers with specific tags:

```typescript
await pushary.notifications.send({
  title: 'VIP Exclusive',
  body: 'Early access to our new collection',
  tags: ['vip'],
})
```

### To All Subscribers [#to-all-subscribers]

Omit targeting to send to all active subscribers:

```typescript
await pushary.notifications.send({
  title: 'Site Maintenance',
  body: 'We\'ll be down for maintenance tonight',
})
```

## Response [#response]

```typescript
interface SendResult {
  success: boolean
  campaignId?: string     // Created campaign ID
  sent: number            // Number of notifications queued
  limits: {
    remaining: number     // Remaining notifications this month
    limit: number         // Monthly notification limit
  }
}
```

## Custom Data [#custom-data]

Pass custom data to handle in `onNotificationClick`:

```typescript
await pushary.notifications.send({
  title: 'New Order',
  body: 'Order #12345 received',
  url: 'https://example.com/orders/12345',
  data: {
    orderId: '12345',
    type: 'order_confirmation',
  },
})
```

Access in client SDK:

```typescript
createPushary({
  siteKey: 'pk_xxx',
  onNotificationClick: (url, data) => {
    if (data.type === 'order_confirmation') {
      trackOrderView(data.orderId)
    }
  },
})
```

## Rate Limits [#rate-limits]

Notifications are subject to your plan's rate limits:

| Plan       | Notifications/Month |
| ---------- | ------------------- |
| Free       | 2,000               |
| Starter    | 25,000              |
| Growth     | 100,000             |
| Enterprise | Unlimited           |

Check remaining limits in the response:

```typescript
const result = await pushary.notifications.send({ ... })

if (result.limits.remaining < 100) {
  console.warn('Running low on notifications')
}
```

## Error Handling [#error-handling]

```typescript
try {
  await pushary.notifications.send({
    title: 'Hello',
    body: 'World',
    subscriberIds: ['sub_123'],
  })
} catch (error) {
  if (error.message.includes('limit exceeded')) {
    // Handle plan limit
  } else if (error.message.includes('No subscribers')) {
    // Handle no targets
  }
}
```