Bento Node.js Guide

Wire `@bentonow/bento-node-sdk` into your API routes, queue workers, or background jobs so events, subscribers, and emails stay in lockstep with your product.

Install Bento's Node.js SDK, track server events, sync subscribers, and send transactional emails from any Node, Bun, or serverless runtime.

Getting Started

Step 1

Install the Bento Node.js SDK

Install the SDK with your framework’s package manager and keep it alongside your other HTTP clients so configuration, logging, and credentials stay centralized.

Using NPM

bash
npm install @bentonow/bento-node-sdk --save

Using Yarn

bash
yarn add @bentonow/bento-node-sdk

Using Bun

bash
bun add @bentonow/bento-node-sdk

Step 2

Configure the client

Store your Site UUID, publishable key, and secret key in environment variables or a secrets manager. Register the Bento services during your app’s bootstrap so every controller, job, or command reuses the same client.

basic_setup

javascript
const { Analytics } = require('@bentonow/bento-node-sdk');
// or using ES modules
// import { Analytics } from '@bentonow/bento-node-sdk';

const bento = new Analytics({
authentication: {
publishableKey: 'YOUR_PUBLISHABLE_KEY',
secretKey: 'YOUR_SECRET_KEY',
},
siteUuid: 'YOUR_SITE_UUID',
// Optional: Set to true for verbose error logging
logErrors: false,
});

Beginner Guide

Tracking your first event

Events are the fastest way to add subscribers, kick off automations, and capture context in a single API call. Track onboarding milestones, purchases, or page views, then personalize with Liquid.

  • Pair every event with subscriber metadata so flows can branch without extra imports.
  • Include transactional context (cart items, amounts, IDs) for downstream personalization.
  • Prefer events over bespoke subscriber mutations—automations can add tags or update fields later.
Detail Liquid tag
Product details {{ event.details.product.size }}
Purchase amount {{ event.details.value.amount }}
Transaction ID {{ event.details.unique.key }}
Payment method {{ event.details.value.payment_method }}

Track a simple page view

javascript
await bento.V1.track({
  email: 'user@example.com',
  type: '$pageView',
  details: {
    url: '/home',
    title: 'Home Page',
  },
});

Track a form submission

javascript
await bento.V1.track({
  email: 'user@example.com',
  type: '$formSubmitted',
  details: {
    formName: 'Newsletter Signup',
    source: 'Homepage',
  },
});

Managing subscribers

You can still create, tag, or unsubscribe subscribers directly when you need to correct data or build admin tooling. Keep these calls reserved for operational work—events remain the preferred entry point.

Create a new subscriber

javascript
await bento.V1.addSubscriber({
  email: 'new@example.com',
  fields: {
    firstName: 'Jane',
    lastName: 'Doe',
  },
});

Tag a subscriber

javascript
await bento.V1.tagSubscriber({
    email: 'user@example.com',
    tagName: 'Newsletter',
  });

Unsubscribe A subscriber

javascript
await bento.V1.removeSubscriber({
    email: 'user@example.com',
  });

Common use cases

  • Track onboarding, purchase, and lifecycle events so automations fire with one API call.
  • Tag subscribers in response to events rather than separate API loops.
  • Store contextual data (cart contents, plan tier) for segmentation and personalization.

Tracking a user login "Event"

javascript
await bento.V1.track({
  email: 'user@example.com',
  type: '$login',
  details: {
    method: 'password',
    device: 'mobile',
  },
});

Updating user information

javascript
await bento.V1.updateFields({
  email: 'user@example.com',
  fields: {
    lastLoginDate: new Date(),
    accountStatus: 'active',
  },
});

Adding multiple tags to a subscriber

javascript
const tags = ['Premium', 'Annual Plan', 'Early Adopter'];
  await bento.V1.upsertSubscriber({
    email: 'user@example.com',
    tags: tags,
  });

Intermediate Guide

Custom fields and tags

Combine fields for rich profile data with namespaced tags for segmentation. Tags keep audiences organized (`plan:pro`, `status:vip`) while fields store free-form values like locale or favorite product.

Namespaced tag ideas

subscription:basicsubscription:prosubscription:enterprise

Apply via automations whenever possible so marketing and data teams stay in sync.

Create a new custom field definition

javascript
await bento.V1.Fields.createField({
  key: 'membershipLevel',
});

Get all existing fields

javascript
const fields = await bento.V1.Fields.getFields();
console.log('Available fields:', fields);

Create a new tag

javascript
await bento.V1.Tags.createTag({
  name: 'Power User',
});

Get all existing tags

javascript
const tags = await bento.V1.Tags.getTags();
console.log('Available tags:', tags);

Tracking purchase events

Always include a `unique` key (order ID, cart token, etc.) so Bento dedupes purchases. Capture cart contents and totals to power LTV reporting and Liquid personalization.

Track a purchase event to monitor customer lifetime value

javascript
await bento.V1.trackPurchase({
  email: 'customer@example.com',
  purchaseDetails: {
    unique: {
      key: 'order-123' // Unique order identifier
    },
    value: {
      amount: 9999, // Amount in cents
      currency: 'USD'
    },
    cart: {
      items: [
        {
          product_id: 'prod-456',
          product_name: 'Premium Widget',
          product_price: 9999,
          quantity: 1,
          product_sku: 'SKU-456',
        }
      ]
    }
  },
});

Namespaced tags

Reserve tags for segmentation keys (e.g., `product:analytics`). Use fields when you need arbitrary values or timestamps. This keeps your marketing taxonomy predictable.

Advanced Guide

Batch operations

Batch endpoints cover more than 80% of API work. Keep payloads lean, batch 200–300 records, and retry only failed chunks.

Import multiple subscribers at once

javascript
await bento.V1.Batch.importSubscribers({
  subscribers: [
    {
      email: 'user1@example.com',
      firstName: 'Alice',
      company: 'Acme Inc'
    },
    {
      email: 'user2@example.com',
      firstName: 'Bob',
      company: 'Beta Corp'
    },
    // ... up to 1,000 subscribers
  ],
});

Import multiple events at once

javascript
await bento.V1.Batch.importEvents({
  events: [
    {
      email: 'user@example.com',
      type: '$login',
      date: new Date('2023-01-01')
    },
    {
      email: 'user@example.com',
      type: '$purchase',
      details: {
        unique: { key: 'order-123' },
        value: { currency: 'USD', amount: 9999 }
      }
    },
    // ... up to 1,000 events
  ],
});

Transactional emails

Great candidates

  • Onboarding confirmation or welcome sequences
  • Password reset and login verification links
  • Payment, receipt, or fulfillment notices
  • Account or compliance notifications

Avoid using transactional for

  • CC / BCC workflows or multi-recipient fan-out
  • Attachments (link to files instead)
  • Marketing, promotional, or newsletter content
  • Bulk announcements where unsubscribes must be honored

Send a transactional email

javascript
await bento.V1.Batch.sendTransactionalEmails({
  emails: [
    {
      to: 'recipient@example.com', // just the email, recipient name is ignored.
      from: 'sender@example.com', // MUST be an existing Author in your account (Emails -> Authors)
      subject: 'Welcome {{ name }}!',
      html_body: '<p>Hello {{ name }}, welcome aboard!</p>',
      transactional: true, // Set to true to send as a transactional email IF you want to ignore if the user has unsubscribed. USE WITH CAUTION!
      personalizations: {
        name: 'John Doe',
        accountType: 'premium'
      }
    }
  ]
});

Subscriber updates

Events remain the preferred way to create and update subscribers—they trigger flows and can mutate tags or fields downstream. Use direct endpoints when workflows demand determinism.

Events-first creation

Create a subscriber when they sign up

javascript
await bento.V1.track({
  email: 'new-user@example.com',
  type: '$subscribe',
  fields: {
    firstName: 'Jane',
    lastName: 'Doe',
    signupSource: 'website'
  }
});

Create a subscriber when they make a purchase

javascript
await bento.V1.track({
  email: 'customer@example.com',
  type: '$purchase',
  details: {
    unique: { key: 'order-123' },
    value: { amount: 9999, currency: 'USD' }
  },
  fields: {
    firstName: 'John',
    lastName: 'Smith',
    customerType: 'new'
  }
});

Direct creation options

Create a single subscriber (email only)

javascript
await bento.V1.Subscribers.createSubscriber({
  email: 'user@example.com'
});

Import multiple subscribers

javascript
await bento.V1.Batch.importSubscribers({
  subscribers: [
    {
      email: 'user1@example.com',
      firstName: 'Alice',
      company: 'Acme Inc',
      membershipTier: 'premium'
    },
    {
      email: 'user2@example.com',
      firstName: 'Bob',
      company: 'Beta Corp',
      membershipTier: 'basic'
    }
  // Can include up to 1,000 subscribers per request
  ]
});

Event-driven profile updates

Update subscriber when they update their profile

javascript
await bento.V1.track({
  email: 'user@example.com',
  type: '$subcription_change',
  fields: {
    subscriptionTier: 'premium',
  }
});

Single attribute tweaks

Add or update a single field

javascript
await bento.V1.Commands.addField({
  email: 'user@example.com',
  field: {
    key: 'membershipLevel',
    value: 'premium'
  }
});

Add a tag

javascript
await bento.V1.Commands.addTag({
  email: 'user@example.com',
  tagName: 'VIP'
});

Remove a field

javascript
await bento.V1.Commands.removeField({
  email: 'user@example.com',
  fieldName: 'temporaryStatus'
});

Batch updates

Update multiple subscribers

javascript
await bento.V1.Batch.importSubscribers({
  subscribers: [
    {
      email: 'user1@example.com',
      membershipTier: 'gold',
      accountStatus: 'active',
      lastRenewalDate: new Date()
    },
    {
      email: 'user2@example.com',
      membershipTier: 'silver',
      accountStatus: 'pending',
      trialEndsAt: new Date(Date.now() + 30*24*60*60*1000) // 30 days from now
    }
  ]
});

Specialized operations

Change a subscribers email address

javascript
await bento.V1.Commands.changeEmail({
  oldEmail: 'old@example.com',
  newEmail: 'new@example.com'
});

Update all fields at once

javascript
await bento.V1.updateFields({
  email: 'user@example.com',
  fields: {
    firstName: 'Updated',
    lastName: 'Name',
    address: {
      street: '123 Main St',
      city: 'New York',
      state: 'NY',
      zip: '10001'
    },
    preferences: {
      theme: 'dark',
      notifications: true
    }
  }
});

Unsubscribe a user

javascript
await bento.V1.removeSubscriber({
  email: 'user@example.com'
});

Utility features

Validate addresses, guess gender, geolocate IPs, or check blacklists without integrating third-party APIs. These helpers are rate-limited—cache responses when you can.

Validate Email

javascript
const isValid = await bento.V1.Experimental.validateEmail({
  email: 'user@example.com',
  ip: '192.168.1.1',
  userAgent: 'Mozilla/5.0...',
});

Guess Prediction (for personalization)

javascript
const genderInfo = await bento.V1.Experimental.getGender({
  name: 'Alex',
});
console.log(genderInfo); // { gender: 'male', confidence: 0.78 }

Geolocate IP Address

javascript
const location = await bento.V1.Experimental.geolocate({
  ip: '208.67.222.222',
});
console.log(location); // { city_name: 'San Francisco', country_name: 'United States', ... }

Check Domain/IP blacklist

javascript
const blacklistInfo = await bento.V1.Experimental.checkBlacklist({
  domain: 'example.com',
  // Or check an IP: ip: '192.168.1.1'
});

API Reference

The Node.js SDK mirrors Bento's REST resources—most services expose async methods for batch operations plus convenience helpers for validation.

Subscriber operations

  • Find subscribers

    await client.getSubscribers(params)

    View docs
  • Create subscriber

    await client.createSubscriber(payload)

    View docs
  • Run subscriber command

    await client.runCommand(commands)

    View docs

Tags + fields

Events & utility

  • Track events

    await client.track(event)

    Events API
  • Send transactional email

    await client.sendEmails(emails)

    Emails API
  • Validate data

    await client.validateEmail({ email })

    Utility

Troubleshooting

Not Authorized

Verify publishable + secret keys and ensure the team member still has access.

Rate Limited

Implement exponential backoff for batch operations and honor Retry-After headers.

Network Errors

Confirm outbound traffic can reach app.bentonow.com and retry transient socket resets.

Payload Exceptions

Double-check Author emails and ensure payloads match the documented schema.

Debugging tips

  1. Enable `logErrors: true` in your SDK config to capture payloads and request IDs inside your logger.
  2. Wrap every API call in try/catch (or a queue job with retries) so transient network/rate-limit failures never crash the app.
  3. Keep batch imports to 200–300 records until payloads validate cleanly, then scale toward 1,000 when you're confident.

FAQ

Can I use this SDK in a browser environment?

No. The Node SDK expects your publishable key, secret key, and Site UUID—credentials that must stay on the server. Use the JavaScript SDK for browser analytics.

How do I handle rate limiting?

Catch `RateLimitedError`, log it, and retry with exponential backoff. Queue workers make this easier because you can delay or requeue failed jobs.

What's the maximum batch size for importing subscribers or events?

The API accepts up to 1,000 subscribers or events per call. In practice 200–300 keeps requests snappy and easier to retry.

How do I track anonymous users?

Every event currently requires an email address. Capture emails early (gated content, waitlists, etc.) so you can identify people before sending events.

Is the SDK compatible with TypeScript?

Yes. The package is written in TypeScript and ships type definitions for every client, request, and response.

How can I contribute to the SDK?

Open issues or pull requests on github.com/bentonow/bento-node-sdk or share ideas in the Bento Discord server.

Contribute or debug further

The Node.js SDK is open source. Report bugs, request features, or open pull requests at https://github.com/bentonow/bento-node-sdk. Keep your local tooling on Node.js 18+ to match production builds.

Need the original Markdown? Open raw file