PHPackages                             monoverse/voicebot-laravel-sync - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. monoverse/voicebot-laravel-sync

ActiveLibrary[API Development](/categories/api)

monoverse/voicebot-laravel-sync
===============================

Sync your Laravel catalog and content to VoiceBot for an AI sales assistant — signed, incremental, swiss-watch reliable.

v0.5.1(1mo ago)019proprietaryPHPPHP ^8.2

Since Jun 14Pushed 1mo agoCompare

[ Source](https://github.com/Monoverse1/voicebot-laravel-sync)[ Packagist](https://packagist.org/packages/monoverse/voicebot-laravel-sync)[ RSS](/packages/monoverse-voicebot-laravel-sync/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (26)Versions (9)Used By (0)

monoverse/voicebot-laravel-sync
===============================

[](#monoversevoicebot-laravel-sync)

Sync your Laravel catalog and content to **VoiceBot** for an AI sales assistant — signed, incremental, swiss-watch reliable.

This package is a **producer client** for the VoiceBot canonical signed-ingest contract (ADR-045 / ADR-055). It pairs with VoiceBot once, then pushes your products, categories, pages and more over an HMAC-signed protocol: a full snapshot for the baseline and small incremental deltas after that. Voice stays on the server — this package never embeds any LLM SDK; it only streams your data to the ingest endpoint.

- **Signed** — every request is HMAC-SHA256 signed (protocol v2); the shared secret is stored **encrypted at rest** and never logged or printed.
- **Incremental** — a per-entity watermark means deltas push only what changed.
- **Memory-safe** — snapshots stream as gzipped NDJSON straight from disk; the full catalog is never held in memory.
- **Adaptive** — point each entity kind at your model with a small column map, or bind a fully custom source.

---

Requirements
------------

[](#requirements)

- PHP 8.2+ (Laravel 13 requires PHP 8.3+)
- Laravel 10, 11, 12 or 13
- An `APP_KEY` (used to encrypt the stored shared secret)

Install from Packagist
----------------------

[](#install-from-packagist)

The package is published on public [Packagist](https://packagist.org/packages/monoverse/voicebot-laravel-sync)as `monoverse/voicebot-laravel-sync` — install it with a plain Composer require:

```
composer require monoverse/voicebot-laravel-sync
```

Publish the config and run migrations (auto-discovery registers the service provider):

```
php artisan vendor:publish --tag="voicebot-config"
php artisan migrate
```

The package registers its migrations, so a plain `php artisan migrate` creates three tables: `voicebot_connections` (encrypted secret), `voicebot_sync_state` (per-kind watermark) and `voicebot_dead_letter` (parked failures). No migration publish step is required; publish them only if you want to customise the schema.

> The ingest base URL must be `https://` (plaintext `http://` is rejected — an HMAC over http would leak the signature). `localhost` / `127.0.0.1` are allowed for local dev.

Pair
----

[](#pair)

Copy your **publishable key** (`pk_...`) from your VoiceBot dashboard, then:

```
php artisan voicebot:pair pk_...
```

The command stores your tenant id + shared secret (encrypted) and prints the tenant id. **The shared secret is never printed or logged.** For non-interactive CI, set `VOICEBOT_PUBLIC_KEY` and run `php artisan voicebot:pair`.

`VOICEBOT_SITE_URL` (defaults to `APP_URL`) is sent as the storefront `site_url` and must match the domain bound to the key in your dashboard (apex/`www` are treated as equal). A mismatch fails with a clear `domain_mismatch` hint — point `VOICEBOT_SITE_URL` at the registered storefront domain.

> **Legacy fallback.** The earlier one-time pair code (`VB-XXXX-XXXX`, env `VOICEBOT_PAIR_CODE`) still works: pass it as the argument or set the env var. Pairing resolves the credential in order: CLI argument → `VOICEBOT_PUBLIC_KEY` → `VOICEBOT_PAIR_CODE`. Already-paired installs keep their stored secret — re-pairing is not required.

Map your models
---------------

[](#map-your-models)

Open `config/voicebot.php` and, for each kind you have, set the model and a column map. Map keys are dot paths under `payload`; values are either a column name or a closure receiving the model.

```
use App\Models\Product;

'entities' => [
    \Monoverse\VoicebotSync\Dto\EntityKind::Product->value => [
        'enabled' => true,
        'model' => Product::class,
        'updated_at' => 'updated_at',   // watermark column for deltas
        'external_id' => 'id',          // wrapped to "laravel:product:{id}" automatically
        'with' => ['categories'],       // eager-load to avoid N+1 while streaming
        'map' => [
            'payload.name'        => 'title',
            'payload.sku'         => 'sku',
            // MONEY IS INTEGER MINOR UNITS — convert decimals with a closure:
            'payload.price_amount'=> fn ($m) => (int) round($m->price * 100),
            'payload.currency'    => fn () => config('voicebot.currency', 'UAH'),
            'payload.description' => fn ($m) => trim(strip_tags((string) $m->description)),
            'payload.permalink'   => fn ($m) => route('product.show', $m),
            // CATEGORIES / TAGS ARE SLUG LISTS:
            'payload.categories'  => fn ($m) => $m->categories->pluck('slug')->all(),
            'payload.stock_status'=> fn ($m) => $m->in_stock ? 'instock' : 'outofstock',
        ],
    ],
],
```

### Mapping rules that matter

[](#mapping-rules-that-matter)

- **Money is integer minor units.** `199.99 UAH` → `19999`. Never send a float/decimal.
- **Categories and tags are slug lists** (`['kava', 'napoi']`), not objects.
- **Page `content_text` is plain text** — strip HTML.
- **`external_id`** supplies the raw id only; the package wraps it to `laravel:{kind}:{id}`. Parent references follow the same shape, e.g. `'payload.parent_external_id' => fn ($m) => $m->parent_id ? 'laravel:category:'.$m->parent_id : null`.
- **Soft deletes** are detected automatically (model uses `SoftDeletes`) and pushed as delete ops during deltas. Hard deletes are reconciled by the nightly full snapshot's server-side tombstone pass.

### Custom source (full control)

[](#custom-source-full-control)

For anything the config map can't express, implement `EntitySource` and point the kind's `source` at your class (resolved from the container):

```
use Monoverse\VoicebotSync\Contracts\EntitySource;

final class ProductSource implements EntitySource { /* kind(), upserts(), deletes(), ... */ }

// config/voicebot.php
EntityKind::Product->value => ['enabled' => true, 'source' => \App\VoiceBot\ProductSource::class],
```

`upserts()` and `deletes()` **must** return a streaming `LazyCollection` — never materialise the whole catalog.

Verify before the first push
----------------------------

[](#verify-before-the-first-push)

```
php artisan voicebot:doctor
```

Doctor checks pairing, backend reachability, and — per enabled kind — a resolvable model, a non-empty map, and the backend-required payload fields on a sampled mapped row. It emits one sample mapped record per kind and **pushes nothing**. Fix every `FAIL` before syncing.

Sync
----

[](#sync)

```
php artisan voicebot:sync --full        # complete snapshot (run once, then nightly)
php artisan voicebot:sync               # incremental delta (the cron path)
php artisan voicebot:sync --dry-run     # compute + print what WOULD be sent; push nothing
php artisan voicebot:sync --since="2026-06-01"  # delta from a specific instant (catch-up)
php artisan voicebot:sync --queue       # dispatch to a queue worker and return
```

### Exit codes (cron-friendly)

[](#exit-codes-cron-friendly)

CodeMeaningAction`0`success—`1`transient failure (network/5xx) **or** a delta that dead-lettered opscron alerts, retry next run`2`config error (not paired, empty snapshot, bad model, bad `--since`)a retry won't help; fix configSigned requests retry on connection errors only — never on 429/5xx — because the backend consumes the request nonce before it can return an error status, so a blind retry would be rejected as a replay. The unsigned pair handshake and the snapshot upload (no HMAC) do retry on 429/5xx with backoff that honours `Retry-After`.

Failed delta ops land in `voicebot_dead_letter` (never silently dropped). Re-failures of the same op across runs increment its `attempts`; once it hits `sync.dead_letter_max_attempts` the row is flagged `exhausted` for manual review/replay.

Unpair
------

[](#unpair)

```
php artisan voicebot:unpair
```

Tells the backend to disconnect, then wipes the locally stored tenant id + shared secret. The local credentials are cleared even if the remote call fails, so you can always re-pair. Run this before pairing a different tenant.

Schedule
--------

[](#schedule)

**Prerequisite:** a system cron entry that ticks Laravel's scheduler every minute:

```
* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1
```

### Laravel 11 / 12 — `routes/console.php`

[](#laravel-11--12--routesconsolephp)

```
use Illuminate\Support\Facades\Schedule;

Schedule::command('voicebot:sync')->everyFifteenMinutes()->withoutOverlapping();
Schedule::command('voicebot:sync --full')->dailyAt('03:00')->withoutOverlapping();
```

### Laravel 10 — `app/Console/Kernel.php`

[](#laravel-10--appconsolekernelphp)

```
protected function schedule(\Illuminate\Console\Scheduling\Schedule $schedule): void
{
    $schedule->command('voicebot:sync')->everyFifteenMinutes()->withoutOverlapping();
    $schedule->command('voicebot:sync --full')->dailyAt('03:00')->withoutOverlapping();
}
```

Tune cadence to your catalog volatility — the package ships **no** hardcoded schedule.

Entity coverage
---------------

[](#entity-coverage)

`product`, `variation`, `category`, `tag`, `attribute`, `page`, `post`, `cpt`, `menu`, `menu_item`, `site`, `shipping_method`, `payment_method`. Enable only the kinds you have.

Security
--------

[](#security)

- The shared secret is stored **encrypted** (`encrypted` cast over Laravel `Crypt`, keyed by `APP_KEY`) and is **never logged, printed, or returned** in any output.
- Every signed request carries an HMAC over `METHOD\npath\nts\nnonce\nbody_sha256`; the exact bytes hashed are the exact bytes sent, so the server's body check always matches.
- Requests use a fresh 16-byte nonce and a current timestamp (server enforces a 300s replay window). Retries honour `Retry-After`.

Configuration reference
-----------------------

[](#configuration-reference)

All knobs are env-overridable; see `config/voicebot.php`. Key ones:

EnvDefaultPurpose`VOICEBOT_BASE_URL``https://api.monoverse.tech`Ingest base URL`VOICEBOT_PUBLIC_KEY`—Publishable key (`pk_...`) for pair-by-key (preferred)`VOICEBOT_PAIR_CODE`—Legacy one-time pair code (CI fallback)`VOICEBOT_SITE_URL``APP_URL`Sent as `site_url` / `X-VoiceBot-Site-Url`; must match the key's bound domain`VOICEBOT_SYNC_CHUNK_SIZE``200`Rows per DB chunk while streaming`VOICEBOT_SYNC_QUEUE`defaultQueue connection for `--queue``VOICEBOT_CURRENCY``UAH`Convenience for example mapsPublishing (maintainers)
------------------------

[](#publishing-maintainers)

This package lives in the VoiceBot monorepo at `packages/laravel-sync/` and is published to public Packagist via a **read-only subtree split** of this directory into the standalone mirror repo `Monoverse1/voicebot-laravel-sync`. Releases are **tag-driven**: push a monorepo tag `laravel-sync-vX.Y.Z` and the [`publish-laravel-sync` workflow](../../.github/workflows/publish-laravel-sync.yml) splits + mirrors the tag, which Packagist then indexes. The full runbook (one-time Packagist setup, required secrets/vars, the release flow) is in [`PUBLISHING.md`](./PUBLISHING.md); v0.1.0 was seeded manually and is already live on the mirror.

License
-------

[](#license)

Proprietary © Monoverse.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance92

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

8

Last Release

41d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/bf5883d1e04e36eddde5294957319a11b6651efaa525bfb9a7af9c78698f0a0d?d=identicon)[alyokhin-n](/maintainers/alyokhin-n)

---

Top Contributors

[![VladPrime11](https://avatars.githubusercontent.com/u/129660367?v=4)](https://github.com/VladPrime11 "VladPrime11 (4 commits)")

---

Tags

laravelaiecommercecatalogsyncvoicebot

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/monoverse-voicebot-laravel-sync/health.svg)

```
[![Health](https://phpackages.com/badges/monoverse-voicebot-laravel-sync/health.svg)](https://phpackages.com/packages/monoverse-voicebot-laravel-sync)
```

###  Alternatives

[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M348](/packages/psalm-plugin-laravel)[spatie/laravel-health

Monitor the health of a Laravel application

87912.0M177](/packages/spatie-laravel-health)[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M251](/packages/laravel-ai)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M134](/packages/roots-acorn)[eslazarev/wildberries-sdk

Wildberries OpenAPI clients (generated).

293.1k](/packages/eslazarev-wildberries-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
