Blengi logoBlengi
Get started
Changelog

What's new in Blengi

Every shipped change, newest first. Linkable per version — anchor on #vX.Y.Z.

v2.0.0

WordPress & WooCommerce plugin, BYOK, free file parsing, hardened security

New: official WordPress & WooCommerce plugin

A first-party WordPress + WooCommerce plugin now ships with every Blengi deployment. Drop it on any WordPress 6.4+ install running PHP 7.4 or newer, paste a workspace API token, and the streaming chat widget loads on every public page — with content sync, product cards, coupon application, order lookup, and lead mirroring out of the box.

Widget embed. One asynchronous <script> tag injected into wp_footer, scoped to the post types you opt into. Hidden on wp-admin, login, AJAX, REST, XML-RPC, and cron paths. Emits data-page-dir and data-page-locale attributes so the bar mirrors correctly on Arabic, Hebrew, Persian, and Urdu locales.

Content sync. Bulk + delta ingest of posts, pages, custom post types, and WooCommerce products as Blengi knowledge sources. SHA- 256 content hash short-circuits re-indexing on unchanged content, so re-running "Sync now" against a stable site is effectively free.

Page-builder rendering. Elementor, Beaver Builder, Oxygen, and Bricks pages are rendered with the builder's own native API before sync, so the synced HTML reflects what visitors actually see. Divi posts route through the_content with setup_postdata primed so third-party filter callbacks see a valid $post global. A pitchbar_post_content_html filter lets sites post-process the final HTML (strip nav chrome, force a custom template, redact a section) without forking the plugin.

Resumable sync. Each sync pass enforces a 20-second wall-clock budget. When a large site can't finish in one tick, the plugin persists a resume marker as a WordPress transient and schedules a WP-Cron continuation 30 seconds out. Shared hosting with a 30s max_execution_time cap no longer fatals mid-sync. The Plugins admin shows a soft notice while a chunked sync is in flight.

WooCommerce shopper context. When a logged-in WC customer visits a page, the plugin attaches a signed data-shopper-token to the widget script tag. The widget forwards it to /widget/init; Blengi verifies it and bakes wp_user_id + email_hash (never plaintext) claims into the chat session.

Order lookup tool. EcommercePreset's order_status capability maps to a real tool. When the visitor asks about orders, the LLM calls lookup_order(limit, order_number?), which POSTs to /wp-json/pitchbar/v1/orders/lookup over HMAC-signed callback. Returns id, number, status, total, currency, items, tracking URL (AfterShip / ST tracking / generic meta), and the customer's order view URL.

Coupon emission & apply. The plugin's CouponSyncer snapshots publish-status shop_coupon entries after every product sync (max 50), filters expired / over-limit, and posts them to Blengi. The LLM emits <coupon code="WELCOME10" .../> markers in chat replies; the widget renders them as cards with Copy and Apply buttons. Apply stages the code in a 15-minute transient that auto-applies on the visitor's next cart load via the woocommerce_load_cart_from_session hook.

Abandoned cart re-engagement. A new BehaviorRule.kind = "abandoned_cart" lets you re-engage a visitor whose cart has been idle. The plugin's cart-state.js mirrors every WooCommerce added_to_cart / removed_from_cart event into localStorage; the widget polls every 30 seconds and fires when the cart sits past the rule's idle_minutes threshold.

Lead push-back. Every Blengi lead is mirrored to WordPress as a WC customer (when Woo is active) or WP subscriber, with pitchbar_lead_id + pitchbar_conversation_id in user meta so the store owner can correlate. Idempotent on email — re-pushes update, never duplicate.

Workspace API tokens. New /settings/api-tokens admin page issues workspace-scoped bearer tokens with granular abilities (the plugin uses wp:integration). Blengi stores only the SHA-256 hash of the plaintext; revoke instantly invalidates without deleting audit history. Cross-workspace revoke is forbidden.

HMAC signed callbacks. Blengi → plugin calls (order lookup, coupon apply, lead push) are signed with HMAC-SHA256 over the raw body using a per-token shopper_signing_secret. 5-minute replay window. The plugin verifies via constant-time hash_equals.

Super-admin plugin distribution. New /admin/integrations/wordpress page lets super-admins build and download the install-ready zip directly from the Blengi admin. Backed by the new php artisan pitchbar:build-wp-plugin Artisan command.

Improvements

Plugin-load-order race fixed. v1.x of the plugin trusted class_exists('WooCommerce') at plugins_loaded priority 10, which silently disabled WooCommerce features when alphabetical plugin order put pitchbar before woocommerce. v2.0.0 defers every WC-dependent registration to the woocommerce_loaded action (with a did_action fallback for the priority-20+ case), so the race is eliminated.

Coupon enumeration via `shop_coupon` CPT. Replaces the v1.x reliance on wc_get_coupons() (not public WC API on every release) with get_posts(['post_type' => 'shop_coupon']) + new WC_Coupon($id). Works on every WC version since 2.0.

`abandoned_cart` admin trigger. Backend already supported it; the agent Behavior triggers admin page now exposes it in the kind dropdown.

Comprehensive WordPress docs. Seven new documentation pages under the "WordPress & WooCommerce" group: Overview, Install & connect, Content sync, Page builders, WooCommerce deep links, REST API reference, Troubleshooting.

Fixes

Page-builder content reaches the RAG store. Pre-v2.0 the plugin called apply_filters('the_content', $post->post_content) without priming setup_postdata, which made plenty of the_content callbacks early-return on is_null($post). The Elementor / Bricks / Oxygen path was worse — those builders don't store layout in post_content at all, so the synced HTML was empty. The fix detects each builder via postmeta and calls its native renderer.

Sync survives shared hosting timeouts. The 30-second PHP execution cap on shared hosts no longer fatals mid-sync on large catalogs. The resumable sync pattern (transient + WP-Cron continuation) lets a 5,000-post sync finish over multiple ticks.

Conversation cookie lifecycle. The widget writes pitchbar_conv_id on init success so the plugin's CartCouponController::applyPendingCoupon hook can locate the correct staged coupon. Pre-v2.0 the cookie was only set when ?pitchbar_conv arrived as a query parameter, which left chat→cart flows without a correlation key.

New: BYOK (Bring Your Own Keys)

Each workspace can now pay its own Cloudflare / OpenAI / OpenRouter / Qdrant bill. Super-admin flips a global toggle in Settings → System; customers paste their keys at Settings → AI keys. Per-user override (force-on / force-deny / inherit-global) lets you grant or block individual users independent of the global flag.

Strategic for operators reselling Blengi as a SaaS — the more end-customers scale, the less you owe upstream. Workspace-level credentials are encrypted at rest via APP_KEY (workspaces.byok_keys is an encrypted:array cast). Five layers of tenant isolation guarantee workspace A's keys never leak to workspace B's container resolution. See /documentation/byok for the full matrix.

New: file parsing routed through Cloudflare's free toMarkdown

PDF, DOCX, DOC, XLSX, XLS, ODT, ODS uploads now flow through Cloudflare Workers AI's toMarkdown endpoint instead of the in-process Smalot / PhpWord parsers. Cost: zero (toMarkdown for document formats consumes 0 Neurons). Quality: structured markdown out — headings, lists, tables preserved, which feeds the chunker much better than Smalot's best-effort plain text on Word-exported or scanned-with-text-layer PDFs. CSV / MD / TXT stay on the local parsers; HTML / SVG uploads are rejected at the validator. Falls back to Smalot when Cloudflare credentials are absent (BYOK OpenAI customers, fresh installs) or when the Cloudflare call fails.

New: sitemap fan-out + 500-page cap

The default services.crawl.max_pages_per_source bumped from 25 → 500. Buyers adding a 100-URL sitemap silently lost 75 pages. SitemapDiscoverer now handles three input shapes correctly: domain root (probes /sitemap.xml + /sitemap_index.xml), direct sitemap URL (fetched verbatim — previous code appended a second /sitemap.xml), and <sitemapindex> recursion. CMSes that emit a sitemap-index by default (WordPress, Shopify, Webflow) get every page indexed instead of just the homepage. Override the cap via CRAWL_MAX_PAGES_PER_SOURCE in env.

New: always-visible "Talk to a human" button

The widget header carries the button at all times for any agent whose vertical includes the ticket_escalation capability (every preset ships with it enabled by default). Plus HumanIntentDetector matches ~40 intent phrases ("talk to a human", "connect me to an agent", "I need to speak with someone", …) in chat messages and short-circuits the LLM straight into the handoff flow.

New: 2-minute wait for human handoff + notifications

Clicking "Talk to a human" used to fall straight to "no one's around" when no operator was actively monitoring the dashboard. Now: the conversation ALWAYS queues and notifies, the visitor waits up to 2 minutes (WAIT_TIMEOUT_SECONDS = 120), and every workspace member with live_chat_available=true gets a database + mail notification (HumanRequestedNotification). Operators who had the dashboard closed still see the queued conversation in the sidebar on next sign-in.

New: public KB articles

Curated answers can now be published as public knowledge-base pages at /kb/{workspace.slug}/{article.slug}. Each curated answer row carries kb_title + kb_published toggle and an inline publish pill in the admin grid. KB pages render operator-supplied markdown through a hardened converter (App\Support\SafeMarkdown) — raw HTML stripped, javascript: / vbscript: / data: URIs stripped from links.

New: Google Sheets as a knowledge source

Pick a sheet tab, the crawler ingests every row and treats each as a chunk seed. Mirrors the Notion / Google Docs OAuth path.

New: multi-currency + lifetime-deal pricing

Visitors on the marketing pricing page can pick their currency; Stripe Checkout uses the matching price. Lifetime-deal plans render as a separate section under the subscription tiers.

Security hardening

Stored XSS on public KB pages — blocked. Illuminate\Support\Str::markdown defaults to html_input='allow' and allow_unsafe_links=true, which would make <img onerror=...> and [click](javascript:...) in a KB article execute as live JS on every visitor browser. New App\Support\SafeMarkdown helper swaps in html_input='strip' + allow_unsafe_links=false. Ten unit tests pin every payload.

SSRF guard on the crawler. A workspace admin pasting http://169.254.169.254/... (AWS metadata), http://localhost:6379/ (loopback Redis), or http://10.0.0.5/admin (intranet) used to walk straight into the server's internal network on the PlainHttpCrawler fallback. New App\Support\UrlSafetyGuard blocks every private / loopback / link-local CIDR, refuses non-http(s) schemes, and (on crawl jobs) DNS-resolves hostnames to catch rebind attacks where attacker.com points at 127.0.0.1. PlainHttpCrawler also re-validates every redirect hop and disables auto-follow.

Widget Origin re-check on every privileged endpoint. New VerifyWidgetOrigin middleware re-validates the request Origin against the JWT-bound agent's allowed_origins on every /v1/widget/* endpoint except init (which enforces inline before issuing the JWT). Defence-in-depth against stolen JWTs replayed from an unlisted origin.

Browser-level defence headers on every web response. CSP (default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'), HSTS on HTTPS requests (1 year + subdomains), X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin. Scoped to the web middleware group — the widget API is excluded because buyers embed cross-origin.

Upload MIME allowlist. UploadController requires mimes:pdf,docx,doc,xlsx,xls,csv,md,markdown,txt,odt,ods on every uploaded file. .html / .exe / .svg / .zip uploads get a 422 at the validator before the parser sees a byte.

Mass-assignment cleanup on `User`. byok_enabled and default_workspace_id removed from User::#[Fillable]. Both are privilege-bearing — sanctioned admin paths use forceFill after authorisation.

Improvements

Crawler retry policy. CrawlPageJob now distinguishes rate-limited (release-with-fresh-backoff, no retry slot burned), permanent (DNS error, 4xx) — short-circuit via fail() — and transient (5xx) failures. Stops the MaxAttemptsExceededException flood that used to fill production logs whenever a customer indexed a domain with a few dead URLs. Per-job timeout 90s, failOnTimeout=true so timeouts still flip Source rows to failed with customer-readable errors.

Cloudflare Worker cron driver hardened. TickCommand --max-time bumped 25 → 55s (the loop exits before the 60s tick boundary), --job-timeout default 120s, queues processed: crawl,index,default. Resolves the MaxAttemptsExceededException flood reported in production.

PDF reindex works for uploads. UploadController now persists parsed segment text under storage/app/private/uploads/{source_id}/segment-N.txt. The Reindex button reads it back without re-uploading the original. Pre-fix, "Reindex" was a no-op on file uploads because the controller short-circuited on a null document.url.

PDF parser sanitization. Strips PDF stream artifacts, control bytes, and "/Type /Page" leakage that some encoders dump into getText(). Falls back to full-document text when per-page extraction returns empty (common for Word-exported PDFs). Drops segments with <75% printable characters as "no extractable text" instead of indexing garbage.

`pages_total` in source preview reads `services.crawl.max_pages_per_source`. The progress indicator on the Sources preview now reflects the current cap (500 by default) instead of the hard-coded 25 that was stale after the bump.

Cloudflare AI Gateway support. Set CLOUDFLARE_AI_GATEWAY_URL to route Workers AI calls through Cloudflare's gateway — observability, caching, optional per-account rate limits.

Documentation refresh. New /documentation/byok page; security, knowledge, live-chat, allowed-origins, env, and deployment pages all updated to reflect the v2.0.0 changes.

Fixes (operator-reported during v2.0.0)

"AI keys" link surfaced to customers. After a buyer enrolled BYOK from the admin dashboard, their customers had no way to find the /settings/byok-keys form because no nav entry pointed at it. Fixed by adding a conditional sidebar entry driven by the byok.unlocked Inertia shared prop.

"AI keys" link hidden from super_admin. Super-admins were seeing the customer-facing BYOK menu. They manage platform-wide AI credentials at /settings/system instead; the workspace BYOK form is customer-only. The controller hard-404s for super_admin too as belt-and-braces.

Token-count cap precision. Replaced naive mb_strlen / 4 heuristics for the max-tokens budget with a Unicode-aware estimator that distinguishes ASCII (4 chars/token) from CJK (~1 char/token). The plan's max_tokens ceiling now applies correctly to non-English visitor messages.

Sources errors are sanitised before reaching the customer UI. Raw Cloudflare 401 JSON envelopes used to bleed through to the Sources list. SourceErrorPresenter now rewrites them to short friendly lines.

v1.2.0

Smarter follow-ups, multi-CTA cards, and behavior-trigger reliability

New features

Suggested follow-up questions. After every answer, the widget now shows up to three follow-up prompts the visitor can tap to keep the conversation moving. Reduces the dead-end "I don't know what else to ask" drop-off. Generated automatically from the answer's context — no setup needed.

Stage-aware typing indicator. Instead of a generic "..." dot, the widget now tells the visitor exactly what's happening: "Searching your site…" while we pull relevant pages, then "Thinking…" while the AI writes the reply, then the streamed answer. Makes the wait feel deliberate instead of frozen.

Unread badge + soft chime when minimised. If a visitor walks away from a half-finished reply, the widget pops a small red badge on the closed bar and plays a one-time soft tone. They come back, see "1 new message", click, and pick up where they left off. The chime respects the system's "reduce motion" preference and never plays more than once per session.

Up to three CTA cards stack per reply. Earlier the agent could only show a single call-to-action card after an answer ("Book a demo"). Now configure up to three CTAs with priorities — all matching ones render as a clean stack so a visitor asking about pricing sees Pricing, Demo, and Docs together. Pre-fix only the top-priority CTA shipped.

Lead-form trigger strategy. Every agent now has four ways to decide when the inline lead form appears: <strong>Engagement</strong> (default — fires once intent is detected or after a few turns), <strong>First message</strong> (every visitor sees the form on turn one — best for sales-led agents), <strong>Keyword only</strong> (only when the visitor signals real intent like "pricing" / "demo"), and <strong>Never</strong> (chat without lead capture). Pick the one that matches your funnel.

Self-host installation guide. New <code>/documentation/installation</code> page walks operators from a fresh server to a running Blengi deployment — requirements, environment variables, database setup, asset build, Octane/Horizon/Reverb processes, cron, first admin, smoke test, troubleshooting. Covers both host cron and the one-click Cloudflare Cron Worker option.

Simplified Chinese for the WordPress plugin. The Blengi WordPress companion plugin (v2.0.5) now ships with a complete Simplified Chinese (zh_CN) translation pack — 53 strings covering settings, sync buttons, status messages, and admin notices. WordPress automatically loads it when the site or user locale is set to 简体中文.

Bundled standalone documentation in the WordPress plugin. Every plugin zip now includes a single-file <code>documentation.html</code> buyers can open without an internet connection. Mintlify-style reference covering installation, content sync, page builders, WooCommerce, REST API, troubleshooting.

Connected WordPress sites surface per agent. The Integrations page in the dashboard now shows every WordPress site that connected the companion plugin to one of your agents — site URL, plugin version, WooCommerce status, "Last seen X ago". Confirms the integration is live without having to log into each WP install.

Improvements

Existing agents no longer answer "I don't have enough information" with sources indexed. The default similarity threshold for newly created agents drops from 0.78 to 0.5 to match how Cloudflare's embedding model scores real matches (it scores lower than OpenAI's by design). Visitors now get answers from the content you indexed instead of the fallback line. <strong>Note for existing agents:</strong> agents created before this release may still be on 0.78. If you see the "no information" fallback, open the agent's settings and lower the threshold to 0.5.

Analytics page survives stale-schema deploys. Earlier a single broken column on the analytics dashboard would return a generic "Internal Server Error" page. The dashboard now degrades gracefully — missing data shows as zeros, a yellow banner explains "some metrics couldn't be loaded — the most likely cause is a pending database migration", and the rest of the page renders normally.

Customer-safe wording on crawl errors. When a crawl failed — service hiccup, missing page, blocked site — the Sources list used to show the raw upstream message (sometimes including JSON envelopes from Cloudflare with status codes and error bodies). The customer-facing column now shows short friendly lines like "We couldn't reach this page" or "The crawl service is busy right now — we will retry automatically." Operators still see the full raw message under <strong>Show details</strong>.

PDF, TXT, DOCX uploads now reliably index. A queue worker configuration was missing the indexing lane for file uploads, so PDFs and text documents could sit in "pending" indefinitely on some deployments. Knowledge uploads from now on flow through correctly and finish indexing within a minute.

Fixes

Behavior triggers now actually fire. The widget's behavior triggers — exit-intent, idle, scroll-depth, time-on-page, abandoned-cart — were silently disabled in earlier releases. Rules configured in the admin saved fine and showed up in the live init payload, but a teardown bug detached the listeners milliseconds after they attached, so they never ran. Buyer Dovydas's "behavior triggers don't work" report turned out to be real and a critical regression. Fixed; every trigger kind now fires as documented. No reconfiguration needed.

Three configured CTAs no longer collapse to one. A buyer reported that adding three CTAs to an agent only ever rendered the top-priority one. The selector now returns up to three matching CTAs and the widget stacks all of them. Existing agents already configured with multiple CTAs see them all without any setup change.

No raw Cloudflare 401 JSON leaks into the Sources list. Auto-indexed sources from visitor pages would sometimes show the raw upstream error envelope (Cloudflare Browser Rendering JSON with status codes, error codes, and authentication messages) directly in the customer-facing column. The customer now sees a sanitized "the crawl service is temporarily unavailable on your workspace" message instead; the raw message stays available under the operator's Show details toggle.

Analytics dashboard no longer 500s on stale schema. Companion to the Improvements section above — the actual cause of every "Internal Server Error" report on <code>/app/analytics</code> we'd seen in the last few weeks.

v1.1.0

Visual workflow editor, customizable lead form, and widget polish

New features

Visual workflow editor. Build branching chat flows on a drag-and-drop canvas. Add trigger nodes, message bubbles, ask-and- capture questions, conditional branches, lead tags, and outbound webhooks. Branches fan out visually and reconnect cleanly. If you prefer a keyboard-first view, every workflow round-trips to a linear form editor — same data, two ways to edit it.

Conditional logic in workflows. Three new step types let your flows do real work: branch on a captured answer (equals, contains, starts-with, is-empty, default), tag the lead so your CRM can route it, and POST conversation state to any external URL. Match modes any / all / exact let keyword triggers fire on the right intent instead of every loose match.

Custom lead-form builder. Each agent now has its own lead-form schema. Pick from text, email, phone, long-text, dropdown, and checkbox fields. Reorder them, mark which are required, set placeholders and length limits. Four presets ship out of the box (Classic, B2B SaaS, Support, GDPR-friendly). The same schema renders in both the inline mid-conversation form and the new pre-chat gate so visitors see a consistent form everywhere.

Pre-chat lead gate. Optional per-agent toggle. When on, visitors see your lead form before the chat surface unlocks — the conversion pattern Intercom and Drift have used for a decade. Once submitted, the chat opens on the same panel with no reload. Returning visitors don't see the gate again.

Widget position picker. Three placement options for every agent: centered bar across the bottom (the default), floating bubble in the bottom-right corner (the classic Intercom / Drift layout), or bottom-left (mirrored, useful when the right edge of the page is busy with other widgets).

Restricted paths. Per-agent list of URL patterns where the widget should not appear. Drop in /admin/*, /checkout, or /account/* and the widget stays out of those flows without touching your site code.

Annual billing. Pricing page now has a monthly / annual toggle that surfaces the savings percentage based on each plan's configuration. Stripe Checkout uses the matching price.

PayPal and Razorpay payment gateways. Stripe is no longer the only checkout option. PayPal works in every market PayPal serves (huge for buyers outside the US/EU); Razorpay covers India + South Asia with native support for UPI, cards, and net-banking. Both gateways slot into the existing plan + subscription model — admins toggle each gateway on or off from Settings → System and visitors pick at checkout. Webhook signature verification on both ends; the captured-lead and receipt emails inherit the same white-label cascade as Stripe.

Per-plan AI controls. Rate limits and max-tokens are now configurable per plan from the admin console, so you can match each tier's generosity to your actual cost.

Live lead notifications. A toast appears in the dashboard the moment a lead lands. Workspace members can opt in to browser-push notifications too, so the alert reaches them even when the dashboard tab is in the background.

Marketing site testimonials and FAQ. The homepage gains an auto-advancing testimonial carousel and a 10-question FAQ accordion. Both are editable from Settings → Marketing — replace the shipped defaults with your own quotes and questions.

Search-engine optimization. Every public page now ships proper SEO out of the box: per-page title and description, canonical URL, Open Graph card for social sharing, Twitter summary card, and structured data (Organization, SoftwareApplication, FAQPage). New /sitemap.xml lists every public route plus your published changelog versions. Updated /robots.txt allows crawlers on public pages and blocks them from admin / API surfaces.

White-label cascade everywhere. Your brand name and logo now flow through outbound billing emails (Stripe, PayPal, Razorpay receipts), the captured-lead email, and the widget's "Powered by" footer attribution.

Lead-email pipeline test. Settings → System → Mail gains a "Send test lead email" button that sends the actual lead-captured notification through your queue worker — catches the missing- worker bug class instantly. The existing raw SMTP test stays for checking mail-server connectivity.

Public marketing site toggle. New "Public marketing site" switch in Settings → Branding. Turn it off and unauthenticated visitors hitting /, /pricing, /changelog, /documentation, etc. are redirected to /login — useful for private / internal SaaS deployments. /privacy, /terms, and the auth flows stay accessible always; search engines are told to skip the entire domain.

Improvements

Workflows page redesigned. The workflows list now matches the rest of the admin: search bar, status filter, sort menu, column visibility toggle, and pagination. Same shape as Agents, Leads, and Conversations.

Customizable widget defaults. Three-tier merge for widget appearance: shipped defaults → platform admin defaults → workspace overrides → per-agent. Set a sensible house style once; customers override only what they want.

Fixes

Widget no longer appears on signed-in admin or customer pages. A two-layer fix: the application's root layout suppresses the demo widget for authenticated users (server-side, can't be misconfigured), and the new Restricted Paths feature gives buyers fine-grained per-page control.

Mobile homepage hero text no longer overflows on narrow viewports (especially iPhone SE).

Settings → Widget is back to a single layout wrap with a two-column form and a live preview that matches the real omnibar.

Brand consistency on the dashboard. The "Set up Blengi in a few steps" sidebar card and the widget's demo pill no longer hard-code the source brand name — both follow your install's configured site title.

"What's new" banner. Fixed a layout bug where the leading "What's new in" text could be clipped behind the sidebar on wide screens. Banner is now mounted inside the content column so the sidebar can't overlap it.

Changelog survives `migrate:fresh`. Release notes now persist to storage/app/private/changelog-entries.json instead of a database table, and auto-seed from version-controlled markdown files at database/changelog-entries/v*.md on first read. New deploys pick up shipped release notes automatically; existing entries are never overwritten.