ابرووا
شروع رایگان
☰ فهرست مستندات · راهنمای Node.js
به‌زودی. Abrova Push به‌زودی فعال می‌شود. این صفحه توضیح می‌دهد چطور کار خواهد کرد؛ هنوز قابل استفاده نیست.
این صفحه هنوز ترجمه نشده — نسخه‌ی انگلیسی نمایش داده می‌شود. English

Node.js SDK Guide

Server-side guide for the Abrova Push Node.js SDK — installation, security (API key + server secret), push, inbox, devices, templates, segments, scheduled, webhooks, analytics.

Node.js 0.1.4Node.js 16+به‌روزرسانی: ۵ آبان ۱۴۰۵

Server-side SDK for sending push notifications, managing inbox, devices, and more from your Node.js backend. For the per-platform SDK reference page, see Node.js SDK reference.

Installation#

The SDK installs from Abrova Center in two steps.

  1. Point the @abrova scope at Abrova Center in your project's .npmrc:
# .npmrc (in your project root)
@abrova:registry=https://center.abrova.ir/api/packages/abrova/npm/
  1. Install the package:
bash
npm install @abrova/push-node

Initialize

typescript
import { AbrovaPush } from '@abrova/push-node';

const abrovaPush = new AbrovaPush({
  apiKey: process.env.ABROVA_API_KEY,             // ab_live_xxx
  serverSecret: process.env.ABROVA_SERVER_SECRET, // ab_srv_xxx (required, server only)
});

Quick Example

typescript
await abrovaPush.push.sendToUser({
  userId: 'user-123',
  title: 'Order Shipped',
  body: 'Your order #1234 has been shipped!',
  data: { orderId: '1234' },
});

Error Handling

typescript
import { AbrovaPush, AbrovaPushError } from '@abrova/push-node';

try {
  await abrovaPush.push.sendToUser({
    userId: 'user-123',
    title: 'Hello',
    body: 'World',
  });
} catch (error) {
  if (error instanceof AbrovaPushError) {
    // error.statusCode, error.message
  }
}

Security — Authentication#

Server-side operations require both API Key and Server Secret for authentication. This ensures that only your backend server can perform privileged operations.

Two Credential Types

  • API Key (ab_live_xxx) — Used by client SDKs (iOS, Android, Flutter, Web). Can be embedded in mobile/web apps.
  • Server Secret (ab_srv_xxx) — Required for all server-side operations. Used by Node.js SDK and direct REST API calls from your backend. Never expose this in client-side code!

Why Both Are Required

  • API Key alone can be extracted from mobile apps
  • Server Secret ensures the request comes from your trusted backend
  • Prevents unauthorized server-side operations even if API key leaks
  • Simplifies security — no need to decide which operations need extra protection

Where to Find Your Credentials

  1. Go to Abrova Console
  2. Create a new project (add the Server / API platform to get a server secret) or select an existing one
  3. The API Key and Server Secret are shown in full at project creation
  4. Save both immediately — they are only shown once!
  5. If lost (or never created), regenerate / generate them from Project Settings → Credentials

Environment Variables

bash
# .env file
ABROVA_API_KEY=ab_live_abc123def456...
ABROVA_SERVER_SECRET=ab_srv_xyz789...
typescript
import { AbrovaPush } from '@abrova/push-node';

const abrovaPush = new AbrovaPush({
  apiKey: process.env.ABROVA_API_KEY,
  serverSecret: process.env.ABROVA_SERVER_SECRET,
});

Example — Order Update Notification

typescript
// services/notification.service.ts
import { AbrovaPush } from '@abrova/push-node';

const abrovaPush = new AbrovaPush({
  apiKey: process.env.ABROVA_API_KEY,
  serverSecret: process.env.ABROVA_SERVER_SECRET,
});

export async function notifyOrderShipped(userId: string, orderId: string, trackingUrl: string) {
  // Send push notification
  await abrovaPush.push.sendToUser({
    userId,
    title: 'Order Shipped!',
    body: `Your order #${orderId} is on its way`,
    data: { orderId, trackingUrl },
    deepLink: `myapp://orders/${orderId}`,
  });

  // Also add to inbox for permanent record
  await abrovaPush.inbox.sendToUser({
    userId,
    content: {
      title: 'Order Shipped',
      body: `Your order #${orderId} has been shipped. Track your package.`,
      deepLink: `myapp://orders/${orderId}`,
    },
    category: 'orders',
  });
}

// Usage in your order controller
app.post('/orders/:orderId/ship', async (req, res) => {
  const { orderId } = req.params;
  const order = await Order.findById(orderId);

  order.status = 'shipped';
  order.trackingUrl = req.body.trackingUrl;
  await order.save();

  await notifyOrderShipped(order.userId, orderId, order.trackingUrl);

  res.json({ success: true });
});

Client vs Server Authentication

OptionTypeRequiredDescription
iOS / Android / FlutterMobile SDK-API Key only
Web BrowserWeb SDK-API Key only
Node.js BackendNode.js SDK-API Key + Server Secret
Direct REST APIcurl / fetch / axios-API Key + Server Secret

Push — Send Notifications#

Send to User

typescript
await abrovaPush.push.sendToUser({
  userId: 'user-123',
  title: 'New Message',
  body: 'You have a new message from John',
  data: { chatId: 'chat-456' },
  deepLink: 'myapp://chat/456',
});

Send to Device

typescript
await abrovaPush.push.sendToDevice({
  deviceId: 'device-uuid',
  title: 'Welcome!',
  body: 'Thanks for installing our app',
});

Send to Multiple Devices

typescript
await abrovaPush.push.sendToDevices({
  deviceIds: ['device-1', 'device-2', 'device-3'],
  title: 'Update Available',
  body: 'A new version is available',
});

Send to Topic

typescript
await abrovaPush.push.sendToTopic({
  topic: 'promotions',
  title: 'Flash Sale!',
  body: '50% off everything today only',
  imageUrl: 'https://example.com/sale.jpg',
});

Send to Segment

typescript
await abrovaPush.push.sendToSegment({
  segmentId: 'vip-customers-segment-id',
  title: 'Exclusive Offer',
  body: 'Just for our VIP customers',
});

Broadcast

typescript
await abrovaPush.push.broadcast({
  title: 'App Update',
  body: 'Check out the new features!',
});

Rich Notifications

typescript
await abrovaPush.push.sendToUser({
  userId: 'user-123',
  title: 'New Product',
  body: 'Check out our latest arrival',
  imageUrl: 'https://example.com/product.jpg',
  actions: [
    { id: 'buy', title: 'Buy Now', action: 'BUY_PRODUCT' },
    { id: 'later', title: 'Save for Later' },
  ],
  deepLink: 'myapp://product/123',
  badge: 1,
  sound: 'notification.wav',
  priority: 'high',
  ttl: 3600, // 1 hour
});

Using Templates

typescript
await abrovaPush.push.sendToUser({
  userId: 'user-123',
  templateId: 'order-shipped-template-id',
  templateVariables: {
    orderNumber: '#1234',
    trackingUrl: 'https://track.example.com/1234',
  },
});

App Identifier (Multi-app Targeting)

typescript
// Send only to a specific app on the device
await abrovaPush.push.sendToUser({
  userId: 'user-123',
  title: 'New Order',
  body: 'Your order is ready',
  appIdentifier: 'com.myshop.app', // only this app receives it
});

appIdentifier If your project has multiple apps (e.g. a shopping app and a chat app), use appIdentifier to target a specific app. This is the bundle ID / package name of the app (e.g. com.myapp.ios).

Response

typescript
const result = await abrovaPush.push.sendToUser({...});

console.log(result);
// {
//   success: 3,
//   failed: 0,
//   messageId: '6f1c…',  // use with abrovaPush.receipts
//   billing: {
//     messagesCharged: 3,   // messages counted for this send
//     remaining: 9997       // free messages left this month
//   }
// }

Inbox#

Signed user tokens.

The read / update calls (getMessages, getMessage, updateStatus, updateMultiple, markAllRead, delete) use the inbox routes the client SDKs call. If the project turns on Require signed user tokens for the inbox (Project Settings → Abrova Push), those routes need a user's x-user-token, which this SDK does not send, so they return 401 (token_required) for user-owned messages. The send* / broadcast calls are not affected.

typescript
// Send to single user
await abrovaPush.inbox.sendToUser({
  userId: 'user-123',
  content: {
    title: 'Welcome!',
    body: 'Thanks for joining us. Here are some tips to get started.',
    imageUrl: 'https://example.com/welcome.jpg',
    deepLink: 'myapp://onboarding',
  },
  category: 'onboarding',
  expiresAt: '2025-12-31T23:59:59Z',
});

// Send to multiple users
await abrovaPush.inbox.sendToUsers({
  userIds: ['user-1', 'user-2', 'user-3'],
  content: { title: 'New Feature', body: 'Check out our latest feature' },
});

// Send to device
await abrovaPush.inbox.sendToDevice({
  deviceId: 'device-uuid',
  content: { title: 'Device-specific Message', body: 'This message is for this device only' },
});

// Broadcast
await abrovaPush.inbox.broadcast({
  content: { title: 'Holiday Sale', body: 'Up to 70% off!' },
  category: 'promotions',
  expiresAt: '2025-01-05T23:59:59Z',
});

// Target specific app
await abrovaPush.inbox.sendToUser({
  userId: 'user-123',
  appIdentifier: 'com.myshop.app',
  content: { title: 'Order Shipped', body: 'Your order is on its way!' },
  category: 'orders',
});

// Get user's inbox — resolves to { messages, total, unreadCount }
const inbox = await abrovaPush.inbox.getMessages({
  userId: 'user-123',
  status: 'unread',
  limit: 20,
  offset: 0,
});

// Mark single message as read
await abrovaPush.inbox.updateStatus('message-id', 'read');

// Mark multiple messages as read
await abrovaPush.inbox.updateMultiple(
  ['msg-1', 'msg-2', 'msg-3'],
  'read'
);

// Mark all as read for a user
await abrovaPush.inbox.markAllRead({ userId: 'user-123' });

Devices#

typescript
// List devices
const devices = await abrovaPush.devices.list();

// Subscribe / unsubscribe to topic
await abrovaPush.devices.subscribeTopic('device-uuid', 'news');
await abrovaPush.devices.unsubscribeTopic('device-uuid', 'promotions');

// Associate / clear user
await abrovaPush.devices.setUserId('device-uuid', 'user-123');
await abrovaPush.devices.clearUserId('device-uuid');

// Delete devices
await abrovaPush.devices.delete('device-uuid');
await abrovaPush.devices.bulkDelete(['device-1', 'device-2', 'device-3']);

// Paginated, filtered list (includes inactive devices unless isActive is set)
const { items, total } = await abrovaPush.devices.listPage({
  platform: 'ios',
  isActive: true,
  page: 1,
  limit: 50,
});

// Values you can filter on (app versions, SDK versions, failure reasons)
const options = await abrovaPush.devices.filterOptions();

// Stop sending to a device but keep its history, and undo it
await abrovaPush.devices.unregister('device-uuid');
await abrovaPush.devices.reactivate('device-uuid');

Templates#

Reusable notification content with variable substitution.

typescript
// Create template
const template = await abrovaPush.templates.create({
  name: 'Order Shipped',
  title: 'Your order {{orderNumber}} has shipped!',
  body: 'Track your package: {{trackingUrl}}',
  variables: ['orderNumber', 'trackingUrl'],
  localizations: {
    fa: {
      title: 'سفارش {{orderNumber}} ارسال شد!',
      body: 'پیگیری مرسوله: {{trackingUrl}}',
    },
  },
});

// List
const templates = await abrovaPush.templates.list();

// Render (preview)
const rendered = await abrovaPush.templates.render(
  'template-id',
  { orderNumber: '#1234', trackingUrl: 'https://track.example.com/1234' },
  'en' // locale
);

// Update / Delete / Duplicate
await abrovaPush.templates.update('template-id', {
  body: 'Updated body text with {{variable}}',
});
await abrovaPush.templates.delete('template-id');
const copy = await abrovaPush.templates.duplicate('template-id');

Segments#

typescript
// Create segment
const segment = await abrovaPush.segments.create({
  name: 'VIP Customers',
  description: 'Customers with more than 10 orders',
  color: '#FFD700',
  filters: [
    { field: 'metadata.orderCount', operator: 'greater_than', value: 10 },
    { field: 'metadata.tier', operator: 'equals', value: 'gold' },
  ],
});

Filter Operators

OperatorDescription
equals / not_equalsExact match / no match
contains / not_containsString (or topics) contains / does not contain
starts_withString starts with
in / not_inValue in / not in array
greater_than / greater_than_or_equalNumeric comparison
less_than / less_than_or_equalNumeric comparison
existsField is set
typescript
// List, get, devices
const segments = await abrovaPush.segments.list();
const segment = await abrovaPush.segments.get('segment-id');
const devices = await abrovaPush.segments.getDevices('segment-id');

// Recalculate
const { deviceCount } = await abrovaPush.segments.recalculate('segment-id');
await abrovaPush.segments.recalculateAll();

Scheduled Messages#

typescript
// Schedule
const scheduled = await abrovaPush.scheduled.create({
  title: 'Reminder',
  body: "Don't forget to complete your purchase!",
  targetType: 'user',
  targetValue: 'user-123',
  scheduledAt: '2025-02-14T09:00:00Z',
  timezone: 'Asia/Tehran',
});

Target Types

OptionTypeRequiredDescription
broadcast--Not required (every device). The 0.1.4 SDK type lists `all`, which the API rejects — pass `broadcast` (cast it, e.g. `targetType: 'broadcast' as any`, until the type is fixed)
user--targetValue = User ID
device--targetValue = Device ID
topic--targetValue = Topic name
segment--targetValue = Segment ID
typescript
// List all scheduled messages
const all = await abrovaPush.scheduled.list();

// List pending only
const pending = await abrovaPush.scheduled.listPending();

// Get specific scheduled message
const message = await abrovaPush.scheduled.get('scheduled-id');

// Update scheduled message
await abrovaPush.scheduled.update('scheduled-id', {
  scheduledAt: '2025-02-15T10:00:00Z',
});

// Cancel scheduled message
await abrovaPush.scheduled.cancel('scheduled-id');

// Delete scheduled message
await abrovaPush.scheduled.delete('scheduled-id');

Webhooks#

typescript
// Create webhook
const webhook = await abrovaPush.webhooks.create({
  name: 'Order Events',
  url: 'https://example.com/webhooks/abrova-push',
  events: [
    'message.sent',
    'message.delivered',
    'message.failed',
  ],
  secret: 'my-webhook-secret',
  maxRetries: 3,
  timeoutMs: 5000,
});

Available Events

OptionTypeRequiredDescription
message.sent--Message was sent
message.delivered--Message was delivered to device
message.failed--Message failed to send
message.no_recipients--Target had no registered devices
device.registered--New device registered
device.unregistered--Device was unregistered
device.updated--Device was updated
typescript
// Test, get logs, regenerate secret
await abrovaPush.webhooks.test('webhook-id', 'message.delivered');
const logs = await abrovaPush.webhooks.getLogs('webhook-id', 50);
const { secret } = await abrovaPush.webhooks.regenerateSecret('webhook-id');

// Manage
const webhooks = await abrovaPush.webhooks.list();
await abrovaPush.webhooks.update('webhook-id', {
  events: ['message.delivered', 'message.failed'],
  isActive: true,
});
await abrovaPush.webhooks.delete('webhook-id');

Analytics#

typescript
// Overview stats
const overview = await abrovaPush.analytics.getOverview();

// overview: {
//   totalDevices: 15000,
//   activeDevices: 12500,
//   totalMessages: 50000,
//   totalDelivered: 48500,
//   totalFailed: 500,
//   deliveryRate: 97,
//   platformDistribution: { android: 8000, ios: 6500, web: 500 }
// }

// Get last 30 days of stats
const daily = await abrovaPush.analytics.getDaily(30);

// daily: [
//   { date: '2025-02-01', sent: 1500, delivered: 1450, pending: 20, failed: 30 },
//   { date: '2025-02-02', sent: 1600, delivered: 1550, pending: 10, failed: 40 },
//   ...
// ]

// Get hourly distribution for last 7 days
const hourly = await abrovaPush.analytics.getHourly(7);

// hourly: [
//   { hour: 0, count: 120 },
//   { hour: 1, count: 80 },
//   ...
//   { hour: 23, count: 150 }
// ]

// App-level stats
const stats = await abrovaPush.analytics.getStats();

// stats: {
//   totalDevices: 15000,
//   activeDevices: 12500,
//   androidDevices: 8000,
//   iosDevices: 6500,
//   totalTopics: 25,
//   topics: ['news', 'promotions', 'updates', ...],
//   totalUsers: 10000
// }

// Recent messages
const recent = await abrovaPush.analytics.getRecent(10);

Delivery receipts

A send's success count means the push service accepted the notification. Receipts show what happened on each device. See Delivery Receipts.

typescript
const { messageId } = await abrovaPush.push.sendToUser({ userId: 'user-123', title: 'Hi', body: 'Hello' });

// Per device, for one message
const receipts = await abrovaPush.receipts.getForMessage(messageId!);
const stats = await abrovaPush.receipts.getMessageStats(messageId!);

// Receipt log with filters
const { items, total } = await abrovaPush.receipts.list({
  status: 'failed',
  platform: 'android',
  startDate: new Date(Date.now() - 7 * 86400_000),
  page: 1,
  limit: 100,
});

// Delivery analytics (default: last 30 days)
const analytics = await abrovaPush.receipts.analytics({ bucket: 'day' });
console.log(analytics.totals, analytics.byPlatform, analytics.topFailureReasons);
این صفحه مفید بود؟
گزارش مشکل