Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bento Laravel Sdk Laravel Package

bentonow/bento-laravel-sdk

A Laravel SDK for Bento that makes it easy to send events, manage subscribers, and trigger transactional emails and automations from your PHP app with a clean, fluent, Laravel-friendly API.

Deep Wiki
Context7

Getting Started

Install the package and publish its config:

composer install bentonow/bento-laravel-sdk
php artisan vendor:publish --provider="Bento\BentoServiceProvider" --tag="config"

That drops config/bento.php in place. Wire it from .env so you never commit secrets:

BENTO_SITE_UUID=your-site-uuid
BENTO_PUBLISHABLE_KEY=pk_...
BENTO_SECRET_KEY=sk_...

The service provider and Bento facade are auto-discovered — there is no manual registration step in config/app.php on Laravel 8+.

Where to look first:

  1. config/bento.php — confirm the env keys the package actually reads (key names differ slightly between releases; trust the config file over the README).
  2. vendor/bentonow/bento-laravel-sdk/src/Facade/Bento.php — the docblock lists every proxied method and is the most reliable API reference you have.
  3. The upstream bentonow/bento-php-sdk client — the Laravel wrapper is thin, so method signatures and return shapes come from there.

First use case: identify a newly registered user and fire a signup event. Drop this into your registration listener:

use Bento\Bento;

Bento::identify($user->email, [
    'first_name' => $user->first_name,
    'last_name'  => $user->last_name,
    'plan'       => 'free',
]);

Bento::track($user->email, 'signup', [
    'source' => 'organic',
]);

If that lands in the Bento dashboard under the subscriber, everything else (tags, transactional email, purchase tracking) is the same two calls with different arguments.

Implementation Patterns

Facade vs. constructor injection

The facade is convenient inside controllers and Blade, but prefer injection in services so the class is testable and the dependency is explicit:

use Bento\Bento;

class OnboardingService
{
    public function __construct(private Bento $bento) {}

    public function welcome(User $user): void
    {
        $this->bento->identify($user->email, ['plan' => $user->plan]);
    }
}

The facade resolves the same singleton from the container, so both paths hit the same client instance configured from config/bento.php.

Push Bento calls behind queued jobs

Every SDK call is a synchronous HTTP request to Bento's API. Never call it inline in a request that also does DB writes — a slow or failing Bento API turns into a slow checkout.

// In your controller/service, after the transaction commits
dispatch(new SyncSubscriberToBento($user))->afterCommit();

Use afterCommit() (or fire from a model event) so you don't identify a user whose registration rolls back.

Model events for subscriber lifecycle

Centralise the identify calls rather than sprinkling them across controllers:

protected static function booted(): void
{
    static::created(fn (User $u) => SyncSubscriberToBento::dispatch($u));
    static::updated(fn (User $u) => SyncSubscriberToBento::dispatchIf($u->wasChanged(['name', 'plan']), $u));
}

The job then does a single identify() with the full field payload. Bento treats identify as upsert-on-email, so repeated calls are safe.

Tags as segmentation primitives

Tags are the cheapest way to drive automations in Bento. Treat them as idempotent add/remove operations:

Bento::addTag($user->email, 'trial');
Bento::removeTag($user->email, 'trial');
Bento::addTag($user->email, 'paid');

Wrap add/remove tag pairs in a single job so a plan change doesn't leave a user with both trial and paid.

Purchase / revenue events

Bento's purchase tracking expects a specific payload shape (unique_key, value, currency, plus cart details). Build it in one place:

Bento::trackPurchase($order->user->email, [
    'unique_key' => (string) $order->id,   // your idempotency handle
    'value'      => $order->total_cents / 100,
    'currency'   => $order->currency,
    'cart'       => $order->items->map(fn ($i) => [
        'product_sku'  => $i->sku,
        'product_name' => $i->name,
        'quantity'     => $i->quantity,
        'price'        => $i->unit_price,
    ])->all(),
]);

unique_key is the only thing preventing duplicate revenue in reports — always set it to a stable order identifier.

Transactional email

Bento hosts the templates, so your Laravel app only sends the trigger and the field payload:

Bento::sendEmail(
    $user->email,
    'Welcome to Acme',            // subject
    view('emails.welcome', compact('user'))->render(), // html body
    config('mail.from.address'),
);

If you're using Bento-hosted templates rather than Laravel views, keep the HTML minimal and let Bento's template variables do the work — merge fields must exist on the subscriber (run identify() first).

Testing

The SDK talks over Guzzle directly, so Http::fake() will not intercept it. Fake at the facade/binding level instead:

Bento::shouldReceive('identify')->once()->with($user->email, Mockery::type('array'));
Bento::shouldReceive('track')->once();

For integration-style tests, bind a fake implementation of the client into the container in a base test case so nothing leaves the suite.

Gotchas and Tips

Custom fields must exist in Bento first. identify() with an unknown field is silently dropped or errors depending on API version — create the field in the Bento dashboard before shipping code that writes it. Keep field names in a PHP enum or constant to avoid typos.

site_uuid is mandatory and easy to miss. Config with only the publishable/secret key will fail at request time, not at boot. If you see 401/404 responses, check this first.

Don't call Bento inside a DB transaction. As above — the HTTP call may succeed while your write rolls back, leaving Bento with a subscriber that doesn't exist in your app. afterCommit() is the fix.

Rate limits and retries. The wrapper doesn't add retry logic. Configure your queue worker with --tries=3 and a backoff, and make the job implement ShouldBeUnique on the user/order id to avoid duplicate identify storms during backfills.

Facade method names track the upstream client, not Laravel conventions. If a method you expect (subscribe, unsubscribe) isn't there, grep the facade docblock — the wrapper typically exposes the raw client method names (addSubscriber, removeSubscriber, addTag).

Config caching. php artisan config:cache bakes the Bento keys in. If you rotate keys in production you must re-run config:cache (or config:clear) — a common "why is it still using the old key" trap.

Extension points. For anything the wrapper doesn't cover, resolve the underlying client from the container and use it directly, or fork the config to add a custom Guzzle client. Avoid editing vendor/ — put an adapter class in app/Services/Bento that wraps the facade so the rest of your code has a stable interface if you ever swap providers.

Observability. Log the email + event name (never the secret key) on failure in the job's failed() method. Bento's API returns useful error bodies — capture $e->getMessage() so support can diagnose without digging through queue payloads.

Unsubscribe handling. Don't try to remove subscribers on unsubscribe — use Bento's unsubscribe mechanism and reflect the state in your own DB. Hard-deleting a subscriber also deletes their event history, which breaks revenue attribution.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus