Bento Ruby Guide

Install `bento-ruby-sdk`, configure `Bento::Configuration` with your keys, and call the `Subscribers`, `Events`, and `Emails` helpers from controllers, jobs, or rake tasks.

Use the Bento Ruby SDK to track events, tag subscribers, and send transactional email directly from Rails apps or any Ruby worker.

Getting Started

Step 1

Install the Bento Ruby 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 Gem

bash
gem install bento-ruby-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.

Client Setup

ruby
require 'bento'

Bento.configure do |config|
  config.site_uuid = 'YOUR_SITE_UUID'
  config.publishable_key = 'YOUR_PUBLISHABLE_KEY'
  config.secret_key = 'YOUR_SECRET_KEY'
end

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

ruby
Bento::Events.track(
  email: 'user@example.com',
  type: '$pageView',
  details: {
    url: '/home',
    title: 'Home Page'
  }
)

Track a form submission

ruby
Bento::Events.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

ruby
subscriber = Bento::Subscribers.find_or_create_by(email: 'new@example.com')
puts "New subscriber: #{subscriber.email}"

Tag a subscriber

ruby
result = Bento::Subscribers.add_tag(email: 'user@example.com', tag: 'Newsletter')
puts "Command result: #{result.success?}"

Unsubscribe A subscriber

ruby
result = Bento::Subscribers.unsubscribe(email: 'user@example.com')
puts "Unsubscribe result: #{result.success?}"

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"

ruby
Bento::Events.track(
  email: 'user@example.com',
  type: '$login',
  details: {
    method: 'password',
    device: 'mobile'
  }
)

Updating user information

ruby
Bento::Events.track(
  email: 'user@example.com',
  type: '$activation',
  details: {
    account: 'active',
    device: 'mobile'
  }
)

Adding multiple tags to a subscriber

ruby
subscribers = [
  {
    email: 'user@example.com',
    tags: ['Premium', 'Annual Plan', 'Early Adopter']
  }
]

result = Bento::Subscribers.import(subscribers)
puts "Batch create result: #{result.success?}"

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

ruby
# This functionality would need to be implemented in a Fields class
field = Bento::Fields.create(key: 'membershipLevel')
puts "New field: #{field}"

Get all existing fields

ruby
# This functionality would need to be implemented in a Fields class
fields = Bento::Fields.all
puts "Fields: #{fields}"

Create a new tag

ruby
# This functionality would need to be implemented in a Tags class
tag = Bento::Tags.create(name: 'Power User')
puts "New tag: #{tag}"

Get all existing tags

ruby
# This functionality would need to be implemented in a Tags class
tags = Bento::Tags.all
puts "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

ruby
Bento::Events.track(
  email: 'customer@example.com',
  type: '$purchase',
  details: {
    unique: {
      key: 'order-123'  # Unique order identifier
    },
    value: {
      amount: 9999,  # Amount in cents
      currency: 'USD'
    },
    cart: {
      items: [
        {
          product_sku: 'SKU-456',
          product_name: 'Premium Widget',
          quantity: 1
        }
      ]
    }
  }
)

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

ruby
subscribers = [
    {
        email: 'user1@example.com',
        first_name: 'Alice',
        fields: { company: 'Acme Inc' }
    },
    {
        email: 'user2@example.com',
        first_name: 'Bob',
        fields: { company: 'Beta Corp' }
    }
    # ... up to 1,000 subscribers
]

result = Bento::Subscribers.import(subscribers)
puts "Batch create subscribers result: #{result.success?}"

Import multiple events at once

ruby
events = [
    {
        type: '$login',
        email: 'user@example.com',
        fields: { date: '2023-01-01' }
    },
    {
        type: '$purchase',
        email: 'user@example.com',
        details: {
            unique: { key: 'order-123' },
            value: { currency: 'USD', amount: 9999 }
        }
    }
    # ... up to 1,000 events
]

Bento::Events.import(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

ruby
Bento::Emails.send_transactional(
  to: 'recipient@example.com',
  from: 'sender@example.com'
  subject: 'Your order #123 has shipped!',
  html_body: '<h1>Your order has shipped!</h1><p>We're happy to let you know that your order is on its way.</p>',
  personalizations: {
    first_name: 'John',
    order_number: '123'
  },
   transactional: true
)

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

ruby
Bento::Events.track(
  email: 'new-user@example.com',
  type: '$subscribe',
  fields: {
    firstName: 'Jane',
    lastName: 'Doe',
    signupSource: 'website'
  }
)

Create a subscriber when they make a purchase

ruby
Bento::Events.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)

ruby
subscriber = Bento::Subscribers.find_or_create_by(email: 'user@example.com')
puts "New subscriber: #{subscriber.email}"

Import multiple subscribers

ruby
subscribers = [
  {
    email: 'user1@example.com',
    fields: {
      membershipTier: 'gold',
      accountStatus: 'active',
      lastRenewalDate: '2023-01-01T00:00:00'
    }
  },
  {
    email: 'user2@example.com',
    fields: {
      membershipTier: 'silver',
      accountStatus: 'pending',
      trialEndsAt: '2023-02-01T00:00:00'
    }
  }
]

result = Bento::Subscribers.import(subscribers)
puts "Batch create subscribers result: #{result.success?}"

Event-driven profile updates

Update subscriber when they update their profile

ruby
Bento::Events.track(
    email: 'user@example.com',
    type: '$subscription_change',
    fields: {
        subscriptionTier: 'premium'
    }
)

Single attribute tweaks

Add or update a single field

ruby
Bento::Subscribers.add_field(
  email: 'user@example.com',
  key: 'membershipTier',
  value: 'premium'
)

Add a tag

ruby
result = Bento::Subscribers.add_tag(
  email: 'user@example.com',
  tag: 'example:tag'
)
puts "Add tag result: #{result.success?}"

Remove a field

ruby
result = Bento::Subscribers.remove_field(
  email: 'user@example.com',
  field: 'temporaryStatus'
)
puts "Remove field result: #{result.success?}"

Batch updates

Update multiple subscribers

ruby
subscribers = [
  {
    email: 'user1@example.com',
    fields: {
      membershipTier: 'gold',
      accountStatus: 'active',
      lastRenewalDate: '2023-01-01T00:00:00'
    }
  },
  {
    email: 'user2@example.com',
    fields: {
      membershipTier: 'silver',
      accountStatus: 'pending',
      trialEndsAt: '2023-02-01T00:00:00'
    }
  }
]

result = Bento::Subscribers.import(subscribers)
puts "Batch update result: #{result.success?}"

Specialized operations

Change a subscribers email address

ruby
result = Bento::Subscribers.change_email(
  old_email: 'old@example.com',
  new_email: 'new@example.com'
)
puts "Change email result: #{result.success?}"

Update all fields at once

ruby
subscribers = [
  {
    email: 'user@example.com',
    first_name: 'Updated',
    last_name: 'Name',
    fields: {
      address: {
        street: '123 Main St',
        city: 'New York',
        state: 'NY',
        zip: '10001'
      },
      preferences: {
        theme: 'dark',
        notifications: true
      }
    }
  }
]

result = Bento::Subscribers.import(subscribers)
puts "Update all fields result: #{result.success?}"

Unsubscribe a user

ruby
result = Bento::Subscribers.unsubscribe(email: 'user@example.com')
puts "Unsubscribe result: #{result.success?}"

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

ruby
validation_result = Bento::Spam.valid?(email)
puts "Email validation result: #{validation_result}"

Guess Prediction (for personalization)

ruby
# This functionality would need to be implemented in a Guesses class
gender_info = Bento::Guesses.gender(name: 'Alex')
puts "Gender prediction result: #{gender_info}"

Geolocate IP Address

ruby
# This functionality would need to be implemented in a Geolocation class
location = Bento::Geolocation.locate(ip: '208.67.222.222')
puts "IP geolocation result: #{location}"

Check Domain/IP blacklist

ruby
# This functionality would need to be implemented in a Blacklist class
domain_blacklist_info = Bento::Blacklist.check(domain: 'example.com')
puts "Domain blacklist check result: #{domain_blacklist_info}"

# Or check an IP instead
ip_blacklist_info = Bento::Blacklist.check(ip: '192.168.1.1')
puts "IP blacklist check result: #{ip_blacklist_info}"

API Reference

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

Subscriber helpers

Events & email

  • Track custom event

    Bento::Events.track(email: email, type: "$login")

    Events API
  • Bulk events

    Bento::Events.import(events)

    Import events
  • Send transactional email

    Bento::Emails.send_bulk(emails)

    Emails API

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. Load credentials from ENV inside `Bento.configure` so keys never leak into git or logs.
  2. Wrap Bento calls in `begin/rescue` blocks, log `request_id` plus payload metadata, and surface failures to whatever alerting you use.
  3. Fan out imports via ActiveJob/Sidekiq and keep batches around 200–300 records so retries stay fast and isolated.

FAQ

Can I use this SDK in a frontend environment?

No. Keep Bento::Configuration and API keys server-side. Use the JavaScript SDK for browser tracking or proxy calls through your Rails controllers.

How do I handle rate limiting?

Rescue 429 responses, log `Retry-After`, and retry with exponential backoff using libraries like `retriable` or Sidekiq retry logic.

rate_limiting

ruby
require 'retriable'

# Using retriable gem for retry logic
Retriable.retriable(
  tries: 3,
  base_interval: 1,
  max_interval: 10,
  multiplier: 2,
  on: [Bento::RateLimitExceededError]
) do
  # Your Bento API calls here
  Bento::Subscribers.import(subscribers)
end
What's the maximum batch size for importing subscribers or events?

The API accepts 1,000 records per call, but 200–300 record chunks are faster to debug and retry. Queue workers when you need to backfill more.

max_batch_size

ruby
# Split large subscriber lists into smaller chunks
def chunk_list(data, chunk_size=300)
  data.each_slice(chunk_size).to_a
end

# Process each chunk
all_subscribers.each_slice(300) do |chunk|
  ProcessSubscriberChunkJob.perform_later(chunk)
end

# In your ActiveJob
class ProcessSubscriberChunkJob < ApplicationJob
  def perform(subscribers)
    Bento::Subscribers.import(subscribers)
  end
end
How do I track anonymous users?

Every event currently needs an email address. Capture emails early (trial signup, checkout, gated content) and hydrate events once the user identifies.

Which Ruby versions are supported?

Ruby 2.6+ works today, but we recommend Ruby 3.0 or newer with Rails 6+ for best performance and TLS defaults.

How can I contribute to the SDK?

Open pull requests or issues on github.com/bentonow/bento-ruby-sdk and coordinate via the Bento Discord for implementation help.

Contribute or debug further

The Ruby SDK is open source. Report bugs, request features, or open pull requests at https://github.com/bentonow/bento-ruby-sdk. Keep your local tooling on Ruby 3.0+ / Rails 6+ (2.7 supported) to match production builds.

Need the original Markdown? Open raw file