Platform Updates

Product Changelog

New features, integration updates, and improvements to the EarlyBird AI platform.

August 2026

Fixed

Internal Staff Call Isolation from the Customer Pipeline

A call from your own staff no longer creates a customer record, so your contact list and your reports stay clean.

Overview

Stopped staff calls from entering the customer pipeline. A roster probe at ring time marks a call as staff-origin, and the downstream writers then decline to mint a customer contact or an intelligence profile for it. The call path itself is unchanged; what changed is what gets written about the call.

Architecture & Key Capabilities

  • Roster probe at ring time: One lookup in the incoming voice handler answers two questions: whether the calling number belongs to a verified, enabled roster person, and whether it matches a staff call-in. The analytics boundary is roster membership rather than the call-in feature flag, and the probe also runs on the non-agent path, so a staff call that lands in voicemail is marked too.
  • Marks declared before the first write: The contract adds a staff-origin mark to the call and contact records, plus a call class and staff call kind on the call record. All are sparse, and the staff call record is the ordinary call row rather than a parallel record family.
  • No customer record for a staff call: A staff-origin call with no booking mints no contact. With a booking the contact stands, since staff can be customers, but carries the mark for reader-side exclusion. The profile that is never built is what keeps roster members out of cohorts, audiences and campaigns structurally rather than by filtering.
  • Classification from the stored tool trail: The summary path classifies a staff call from the server-stored tool trail combined with the staff-origin mark, so a refused attempt by a non-roster caller cannot classify itself as staff. Classified staff calls stamp the class and a short summary and then stop: no identity write, no intelligence profile, no cue tags.
  • Proven by the tests: A case that makes the model throw proves the staff path never consults it, and zero contact rows for a call-out are asserted rather than assumed.
New

Unified Staff Directory with Per-Day Shift Windows

Your whole team and their working hours live in one directory, edited in one place instead of across separate rows and pop-ups.

Overview

Rebuilt the dashboard Staff tab as one directory covering team members and department lines, with a single slide-over drawer replacing the separate rows, modals and inline editors it had grown.

Architecture & Key Capabilities

  • Two card types, one drawer: Team member cards and department line cards now share one slide-over drawer for creating and editing, which replaced the previous schedule link modal, unlinked schedule row and inline services editor.
  • Booking mode per member: Each team member is set to named booking, pooled dispatch or not bookable, and that choice is written through to the bookable staff resource.
  • Weekly working days and shift window: The drawer carries seven day toggles plus a shift start and end time, saved as a working-hours window on each selected day. It appears only for team members who are bookable.
  • Department lines and transfers: Department and desk entries carry a name and a direct line number for call transfers, and team members carry a transfer availability toggle plus an optional call-in PIN for logging absences by phone.
  • Status badges on the card: Cards summarize transfer availability, booking mode and call-in PIN state without opening the drawer.
Integration

Freshsales Provider Mapping for Contacts and Appointments

If you run on Freshsales, your contacts and appointments sync both ways without anyone re-typing them.

Overview

Added Freshsales as a CRM provider: an engine slice and a canonical mapper covering Freshsales contacts and appointments in both directions, an OAuth registry entry, and registration across the discovery, scheduling and write paths.

Architecture & Key Capabilities

  • Contacts and appointments mapping: The mapper translates between the canonical contact and booking shapes and Freshsales contact and appointment records, including full-name splitting, the mobile and work number fallback, account and address fields, and the link that ties an appointment back to its contact.
  • Engine slice: The slice supplies the Freshsales client, contact resolution, staff discovery through the owner selector, an inbound webhook handler, a booking writer, and a paged sync stream. Documented handling covers the webhook join-miss refetch, search index lag, pagination, content hashing on upsert, fatal versus retried HTTP failures, and token refresh.
  • Contact resolution before create: Writes search by email and fall back to phone before creating a contact, so a repeated write does not mint a second record.
  • OAuth registration: A Freshworks authorize and token endpoint pair, a domain connection setting, the contacts, appointments and users scopes, and owner lookup for account identity.
  • Eight unit tests: One suite covering the slice, alongside registration in the provider handler, discovery, scheduler, write host and generic writer registries.
Security

Fail-Closed OAuth State Binding and Enterprise Security Hardening

Connecting an outside tool cannot be hijacked partway through, and a staff member guessing a PIN cannot win fresh attempts by calling back.

Overview

Bound OAuth state to the browser that started the handshake and made the callback fail closed, brought the eleven security measurements that the alarms depend on under declared configuration, and re-keyed the staff PIN attempt budget so it can no longer be reset by redialing.

Architecture & Key Capabilities

  • OAuth state bound to the browser: The sign-in start leg now also drops the state nonce as an HttpOnly, Secure, SameSite=Lax cookie, and the callback requires a timing-safe match before it looks the state up. A missing cookie is the attack case and is rejected, so an unbound callback never reaches, or consumes, a state row. The cookie is cleared on every exit, success or error, making the nonce single use.
  • Per-provider binding on CRM connects: The CRM integration start and callback legs got the same binding under a per-provider cookie name, so two connect popups can be in flight without the second overwriting the first.
  • Security alarms can no longer read green on silence: Eleven alarms were measuring something that existed only as a hand-made change in the account and was written down nowhere. All eleven are now declared in configuration, and each reports a real zero when nothing is happening, so a silent alarm now means nothing happened rather than nothing was measured. One was also moved onto the industry benchmark form rather than a pattern we invented.
  • Staff PIN budget keyed to the identity: The attempt limiter had been counted per call, so hanging up and redialing handed the same roster member a fresh set of guesses. The budget is now keyed on workspaceId, bpId and memberId with the lockout measured from the last failure, and a missing key dimension throws rather than silently degrading to an unbounded budget.
  • Logs encrypted under our own key: Every log group this change declares is encrypted under a key we control rather than a default one.

July 2026

Integration

The Integrations Catalog Opens, with HubSpot Connectable

You can see every tool we plan to connect, which category it falls in, and which one you can connect today.

Overview

Opened the dashboard Integrations tab. HubSpot connects today, and the catalog shows the full set of tools planned around it so you can see whether yours is on the map.

Architecture & Key Capabilities

  • Thirty-five provider catalog: A single frontend catalog module lists 35 providers in four categories: calendars and scheduling, CRM, point of sale, and notifications. It mirrors the backend provider catalog, which stays authoritative for what is actually registered. Most entries carry an industry hint shown on the card, covering home services, real estate, financial advisors, salons and spas, fitness, restaurants and support teams.
  • Only a real connection is offered: A provider is offered for connection only once it is genuinely wired, and the server refuses to start a connection for any that is not, so the catalog can never present a connection that would fail. HubSpot is connectable today.
  • Manage actions available again: Connect, disconnect, settings and manual sync are back behind the manage dialog.
  • Per-business-profile scoping: The catalog records that a connection is keyed to a workspace and a business profile together rather than to the workspace alone, and the tab copy was updated to say so.
New

Registry-Driven Multi-Dashboard Tour and Guidance Platform

The guided tour covers the business dashboard, the admin console and the food dashboard, and picks up where you left it.

Overview

Replaced the single-dashboard product tour with a registry-driven platform covering three dashboards, and deleted the previous implementation in the same change.

Architecture & Key Capabilities

  • Typed tour registry: Tour steps are declared as data per dashboard surface instead of being written into components. Three surfaces shipped: the business dashboard with 12 steps across 4 groups, the admin console with 6 steps, and the food dashboard with 5. Steps find what they point at by a stable marker rather than the fixed element names the old system depended on.
  • Per-surface tour engine: A state machine drives each surface, keeping progress in browser storage under a per-surface key that is invalidated when the registry version changes. It tracks the current step, skipped steps and dismissal, offers a resume prompt, supports keyboard navigation, and emits analytics events for start, step view, skip, completion and dismissal. On the business surface it also preserves the existing server-side setup-guide mark with the original bounded retry, treating a failure there as non-fatal.
  • Spotlight and missing-anchor handling: The spotlight overlay and tooltip cards were ported to the new attribute selectors. When an anchor never appears, the overlay surfaces that step's fallback text after a short timeout instead of hanging, which is how the old system failed on several steps.
  • Old system deleted: The previous tour, its fixed step list and the single oversized controller that fused the tour with the setup checklist, welcome screen and celebration were all removed. The setup checklist is now its own thing with its own progress bar.
  • A tour step cannot point at nothing: A build-time check refuses a step that names a target which does not exist on the screen, so a tour can never stall on a step pointing at something that was renamed or removed.
Faster

Single-Call Authentication Gate and Zero-Request Hydrated Boot

The dashboard opens faster, because moving between pages no longer re-checks your session over the network.

Overview

Cut the per-navigation authentication cost of the dashboard: the server-side AuthGate now makes one backend call instead of two, and a hydrated boot issues no serial auth requests while the session window still has time left.

Architecture & Key Capabilities

  • One gate call per navigation: The gate fetches the user profile once. That response now carries the active business profile's render host and the session expiry, so host correction reads the profile directly and the separate profiles sweep no longer runs on every dashboard navigation.
  • Lazy profiles list: The profile list moved out of server-side rendering into a background client-side load for the selector and dropdown surfaces, with a client-side host-affinity backstop for the residual wrong-host cases.
  • Boot without a serial refresh: The boot-time keepalive slide is skipped while more than seven days of the session window remain, read from the expiry timestamp delivered with the profile.
  • In-place profile switch: Switching business profiles on the same render host from a dashboard page now re-reads the profile in place instead of loading a new document; the switch resolves as in-place or navigating so the calling UI knows whether to return to idle. Cross-host switches keep the single-hop navigation, since subdomain isolation is deliberate.
  • Code-split dashboard tabs: The analytics and marketing tabs load through dynamic imports, so the dashboard shell's first paint no longer carries the charting and campaign bundles.
Fixed

Checkout and Billing Portal Redirects Repaired

Finishing a checkout returns you to a working page instead of an address that does not load.

Overview

Fixed a broken return address that every checkout and billing portal session was built against, and put the whole workspace billing surface under test so the same class of defect cannot return silently.

Architecture & Key Capabilities

  • Dead return address replaced: Checkout success and cancel URLs were assembled from a host that does not resolve, so a completed payment could land on an unreachable page. The address is now supplied by deployment configuration, and the service refuses to start at all if it is missing rather than serving a broken link.
  • Coverage across every billing action: Cancelling and resuming a plan, invoice history, listing payment methods, setting a default, detaching one, opening a customer session, starting a setup intent, and opening the billing portal are each covered by tests that pin their permission checks and their not-yet-provisioned behavior.
  • Ownership checks pinned: Detaching a payment method and setting a default are both tested against ownership, so one workspace cannot act on another's stored card.
Fixed

Appointments Booked by the Voice Agent Are Visible Again

Appointments your voice agent books show up in the dashboard, which they had stopped doing.

Overview

Fixed a defect that hid appointments. The booking path had moved to a permanent appointment identifier, but the dashboard list, the single-appointment view and the CRM mirror were all still looking them up the old way and finding nothing. A second defect in the same area had let two appointments at the same time overwrite one another.

Architecture & Key Capabilities

  • Readers moved to the time index: The dashboard booking list resolver and the CRM booking list function stopped range-querying the old sort key, which encoded the start time and so could never match rows written under the immutable key. Both now query the table's time index between calendar-scan bounds, and the single booking resolver was changed from a prefix query plus filter to a direct item read on the immutable key.
  • CRM writer corrected: The inbound CRM processor was still calling the key helper with two arguments after it had become single-argument, so it silently wrote a key built from the start time alone and dropped the booking id, letting two events at the same time overwrite each other. It now writes the one key shape plus the calendar-scan attribute through the shared key module, which also retired the delete-then-reinsert sequence the mutable key had required.
  • Start-time fallback removed: The booking mapping helpers no longer try to parse a start time out of the sort key. Under the immutable key that fallback would have surfaced the booking id as a bogus timestamp.
  • An unused availability path removed: A second, never-executed availability lookup was deleted after it was confirmed unreachable from the web, phone and desktop apps. It would have offered a fixed set of times with no regard for whether anyone was free; real availability has always come from the live path.

June 2026

Fixed

Live Updates Reach Every Dashboard View

Live updates reach the dashboard reliably, including the workspace-wide views that had been receiving nothing at all.

Overview

Finished moving the dashboard onto its dedicated live-update connection, and fixed two delivery defects the move exposed.

Architecture & Key Capabilities

  • Its own address: Live updates now arrive over a dedicated address rather than one derived from the main interface address, which is a requirement of the connection type rather than a preference.
  • Names checked up front: Each live update stream is mapped to the name the server expects, and an unrecognized name now fails immediately instead of opening a stream that would silently deliver nothing.
  • Workspace-wide delivery fix: An event stamped with a business profile id is now delivered both to listeners registered for that exact id and to listeners registered workspace-wide. Before this change a workspace-wide listener never matched a stamped event, so it received nothing.
  • Nothing else had to change: Each listener records the name it subscribed with and receives its payload under that name, so every screen already reading live updates kept working untouched.
New

Organization Member Invite Acceptance Interface and Route Handler

Someone you invite can accept from the email and set their own password, so you never hand anyone credentials.

Overview

Shipped the frontend half of the workspace invitation flow: an accept page that reads the invitation token from the emailed link and lets the invited person set their own password.

Architecture & Key Capabilities

  • Invite accept page: A new accept route renders a form that reads the token from the query string, strips it out of the visible URL so a refresh cannot replay a stale token, and posts it together with the chosen password to the backend accept endpoint. On success the returned CSRF token is stored and the browser is sent to the profile selector, with no auto-skip for members who can reach only one business profile.
  • Server-decided token outcomes: The form maps each backend rejection to its own message, covering a token that matches no record, one whose lifetime has elapsed, one already used, and a password the pool policy refuses. Validity is decided entirely by the backend; the form carries the token and reports the result.
  • Temporary passwords removed: The team management panel no longer receives or displays a generated temporary password when an invitation is sent. The invitee sets their own password on the accept page instead, so no credential is shown to the inviting admin or sent by email.
  • Signup and navigation cleanup: The business name and industry inputs were removed from the account creation form, and the dashboard navigation group holding the receptionist, business, customer and integration items was renamed from Workspace to Business Profile to match the scope of the items inside it.
New

Shared Workspace Minutes Pool with Per-Profile Invoice Attribution

Several business profiles can share one balance of minutes, and each still shows its own usage on the invoice.

Overview

Added a workspace-level shared minutes pool, so several business profiles can draw from one balance instead of each carrying its own, and gave every profile its own billing identity underneath it.

Architecture & Key Capabilities

  • One balance, many profiles: Pool state lives on a workspace-level row kept separate from the per-profile billing row. Enabling a pool creates a payment identity dedicated to that workspace, guarded so that re-enabling it never creates a second one.
  • Prepaid or pay as you go: A pool runs in prepaid mode, where a top-up grants credit up front, or in pay-as-you-go mode, where overage is billed in arrears against the pool.
  • No rounding loss: Pool balances are held in seconds. Minutes appear only where a figure is displayed or metered, so a long run of short calls cannot quietly round away time.
  • Per-profile attribution on one invoice: At period close, each profile's usage is added to the pool invoice as a descriptive line carrying that profile and the seconds it used. The lines are attribution only and never an additional charge, and each is keyed so a re-run cannot double-add one.
  • Billing identity stays on the billing row: Each profile's payment identity is written onto its billing row alone. Workspace metadata carries none, and the checkout, promotion, and metering paths only ever read it.

May 2026

Security

Sign-In Answers Are Single Use and Cannot Be Replayed

A sign-in response captured off the network is useless to whoever captured it, because it is accepted exactly once.

Overview

Reworked sign-in so a session is issued only in answer to a one-time challenge the platform itself signed. Every answer is recorded and accepted exactly once, so a sign-in response captured off the wire is worthless to whoever replays it.

Architecture & Key Capabilities

  • Server-driven challenge: The challenge is composed, issued and checked entirely on our side, so nothing about which challenge you get is decided by the device asking for it.
  • Default-deny define logic: Define issues a custom challenge on the first call and issues tokens only when the previous challenge came back correct. It rejects any challenge name other than the custom one and fails authentication in every other case, with no short-circuit on user attributes such as verified email.
  • Five verification checks: Verify validates the HMAC-SHA256 signature over the answer with a timing-safe comparison, checks that the subject matches the authenticating user, checks the pool binding claim against the invoking user pool, checks that the payload is neither expired nor issued in the future, and records the token id.
  • Single-use answers: Every answer is recorded the moment it is accepted, encrypted and protected against deletion, and the write is conditional on that answer never having been seen. A replayed answer therefore fails on the second attempt, not the thousandth.
  • Signing key never leaves the server: The key that signs a challenge is held encrypted and readable by exactly two internal callers, and by nothing in the browser.
  • Declared, not hand-built: Everything this change created is declared in configuration rather than left as a hand-made change in the account.
Security

Dashboard Data Requests Are Signed on the Server, Never in the Browser

Your dashboard never holds an access token a script could steal, because every request is signed on the server instead.

Overview

Added the single data route the dashboard talks to. It identifies the browser by a signed session cookie, then obtains short-lived cloud credentials on the server and signs the request itself, so an access token is never handed to the browser where a script could read it.

Architecture & Key Capabilities

  • Cookie and CSRF authentication: The handler verifies an HMAC-signed session cookie with a timing-safe comparison, reads the matching session row from the sessions table, and checks its expiry. Mutations additionally require a CSRF token header bound to the same session by HMAC.
  • Credentials never leave the server: Short-lived credentials are obtained and the request is signed entirely on the server, so nothing the browser holds could be replayed by anyone who took it.
  • Forwarded unmodified: The request and the response both pass through untouched, so the route adds no behavior of its own beyond deciding whether a write needs its extra token.
  • One door, and only one: This route is the only thing permitted to reach the data layer, and a scanner enforces that nothing else ever acquires that permission.
  • Audit logging without payloads: Each request writes an audit entry with the operation name, status, elapsed time, and truncated identifiers. The query body is excluded.
New

Live Dashboard Updates as Records Change

Calls, appointments, contacts and messages appear on your dashboard as they happen, with no refresh.

Overview

Opened a live connection between the dashboard and the platform, so calls, appointments, contacts and messages appear as they are written instead of on a refresh. Each connection is admitted by a single-use ticket and only ever receives records belonging to its own workspace.

Architecture & Key Capabilities

  • Single-use connection tickets: The connect handler redeems a ticket issued by the auth route, rejects expired tickets, deletes the ticket row on redemption, resolves the parent session, and registers the connection with a two-hour expiry.
  • Server-validated subscriptions: The message handler accepts only subscribe, unsubscribe, and ping, and rejects any subscription name outside a fixed set held server-side.
  • Five sources, one second behind: Changes to calls, appointments, users, contacts and messages are picked up within a second of being written and pushed to whoever is watching that view. A batch that fails is split and retried rather than dropped whole, so one bad record cannot stop the other ninety-nine reaching your screen.
  • Scoped delivery and stale connection pruning: The consumer parses the owning scope from each record's partition key and queries only the connections registered under that scope, so a record cannot reach a connection belonging to another workspace. A 410 Gone response from PostToConnection deletes the connection row.
  • Data layer closed to direct callers: The alternate authentication path into the data layer was removed outright, leaving the signed server route as the only way in, and the exclusion that would have let that setting drift back was removed with it.
Security

Encrypted Server-Side Sessions and a Seven-Year Audit Trail

Your session is held on the server under a dedicated encryption key rather than in your browser, and every access lands in an archive that cannot be edited for seven years.

Overview

Built the storage and audit foundation that lets a dashboard session live on the server under a customer-managed key rather than as a token sitting in the browser. The proxy services that run on top of it ship separately.

Architecture & Key Capabilities

  • Customer-managed session key: Sessions are encrypted with a dedicated key that rotates on a schedule and carries a thirty-day deletion window, so a key removal is always recoverable inside that window.
  • Sessions expire on their own: The session store, the live-connection registry, and the short-lived ticket store each carry their own expiry, from ten seconds for a ticket to two hours for a connection. All three are encrypted with that key, and the session store adds point-in-time recovery and deletion protection.
  • Audit trail that cannot be edited: Access is logged for ninety days and archived to date-partitioned storage held under a compliance lock at a seven-year retention, which means an archived record cannot be altered or removed before that term is up, by anyone.
  • Archive refuses weak writes: The archive rejects any request that does not arrive over TLS and any write that is not encrypted.
  • One scoped role per function: Each planned proxy function gets its own execution role rather than a shared one, and the ability to mint data-layer credentials is held by exactly one of them.

April 2026

New

Mobile Dashboard Experience Modernization and Performance Tuning

Navigation on a phone is one drawer, instead of controls scattered across the header and the bottom bar.

Overview

Replaced the mobile dashboard's inline header controls and the bottom navigation's Workspace popup with a single slide-in navigation drawer.

Architecture & Key Capabilities

  • Slide-in navigation drawer: A new MobileSidebarSheet renders a left-side drawer with Overview, Workspace, and Account navigation groups. It locks body scroll while open, restores the prior scroll position on close, and dismisses on backdrop click or the Escape key.
  • Header reduced to a menu button: The mobile header now carries the brand mark and a single hamburger button. The connection status dot, business switcher, theme toggle, and account dropdown were removed from it. The business switcher and theme toggle moved into the drawer footer, next to a support link and the sign-out button.
  • Bottom navigation simplified: The Workspace tab, its animated popup, its outside-click handler, and the icons that became unused were deleted, leaving four tabs.
  • Tab panel scrolling: The active mobile tab panel now scrolls vertically with horizontal overflow clipped.
Fixed

Billing Projection Engine and Complete Webhook Snapshot Handler

Your plan and billing status stay correct after a change, instead of showing a mix of old and new values.

Overview

Replaced the partial subscription updates in the Stripe webhook handlers with a single projection function that takes a Stripe subscription, or null, and returns every subscription field, which is then written over the stored subscription map as one complete object.

Architecture & Key Capabilities

  • One projection function: A single function in the billing layer derives status, plan, included minutes, price and period, usage counters, dunning counters, and lifecycle timestamps from a Stripe subscription, and returns an equally complete object for the no-subscription case.
  • Whole-object writes: A new snapshot writer replaces the entire stored subscription map in one update instead of setting individual attributes, so a field that no longer applies cannot survive from a previous state.
  • State vocabulary reduced: The billing state list went from ten values to seven. Promo was dropped as a discount rather than a status, inactive was merged into canceled, and the cancelled spelling was renamed to match Stripe. The removed names remain as deprecated aliases pointing at the surviving states.
  • Both handlers rewritten: The subscription handler routes all five subscription event types through the projection, passing null on deletion so the record clears completely. The invoice handler projects from Stripe and then applies invoice-specific fields, resetting usage counters on a paid invoice and setting dunning fields on a failed payment.
  • Transition-driven notifications: The handler compares the previous state against the projected one and emits notifications for the transitions it detects, including creation, deletion, cancel scheduled and unscheduled, suspension, restoration, pause, and resume.
Fixed

Stripe State Synchronization Engine and Resilient Billing Projections

Cancelling a plan clears it everywhere, so your billing screen stops showing a plan you no longer have.

Overview

Fixed the billing summary's Stripe sync, which left stale plan, status, period, and dunning fields behind once a subscription was canceled, and made the summary response return empty sections instead of zeros when there is no active plan.

Architecture & Key Capabilities

  • Full subscription lookup: The Stripe list call now asks for every status rather than the first record only, and a subscription with collection paused counts as live alongside active, trialing, and past due.
  • Complete cleanup when nothing is live: With no live subscription the sync writes every subscription field to its cleared value, covering the plan, the subscription and price identifiers, the period dates, usage counters, lifecycle timestamps, and dunning fields, rather than setting the status alone. The prior plan is recovered from the canceled subscription and kept for later reference.
  • Lifecycle fields cleared on an active sync: The canceled and cancel-scheduled timestamps are cleared when a live subscription is found, and the paused, suspended, and dunning fields are cleared whenever the derived status is not the matching one.
  • Nullable response sections: The summary response returns an empty plan, usage, period, dunning, and alerts section when there is no active plan, so the interface can hide those blocks rather than render zeros against no plan. The flat fields the existing frontend reads are kept alongside the new sections.

March 2026

New

Outbound Calls Behind a Nine-Check Consent Gate

Outbound calls reach only contacts who consented, inside legal calling hours, so your outreach cannot put you on the wrong side of a rule.

Overview

Added the outbound calling path. Every outreach request clears a nine-check consent and timing gate before a number is ever dialed, and a blocked request records why and is dropped.

Architecture & Key Capabilities

  • Nine-check compliance gate: Before any dial, the request must clear the workspace outreach toggle, a daily call cap held in function memory, the contact do-not-call flag, a phone number on file, a prior express written consent record that has not been revoked, a 30-day frequency cap, a 14-day recent-interaction cap, the absence of a confirmed or pending upcoming booking, and an 8am to 9pm Eastern calling window. A blocked request records its reason and is dropped rather than dialed.
  • Scoped data access: Contact, booking, and history reads each go through a separate short-lived session, scoped to your workspace and issued for fifteen minutes, so each read reaches only what that scope covers.
  • Per-call prompt construction: The function composes a first message and system prompt from the contact name, business name, agent name, and outreach reason, with the AI disclosure written into the opening line, and applies both to that call alone.
  • Call record on dial: A successful dial writes an outbound call row keyed by the returned call identifier, carrying direction, status, customer phone, outreach type, and timestamps.
Security

Call Intelligence Moved onto Isolated Private Compute

Your call data is processed on machines with no public address, in a sandbox scoped to your workspace alone, with phone numbers and emails masked before anything is logged.

Overview

Moved the daily intelligence sweep and the analytics aggregation off shared short-lived functions and onto dedicated machines that sit on a closed network with no public address, run each job in its own resource-capped sandbox scoped to one workspace, and mask phone numbers and email addresses before any line reaches a log.

Architecture & Key Capabilities

  • Job-driven, and it scales to nothing: Machines pull from a job queue and the fleet drops to zero when there is no work, growing only when the queue backs up. A daily sweep and a six-hourly aggregation feed it. A job that fails three times is set aside rather than retried forever, and an alert fires the moment one lands there.
  • Per-job isolation: Each job runs in its own sandbox with hard processor and memory ceilings, and reads your data through a session scoped to your workspace, so one workspace's job cannot see another's data even on the same machine.
  • Closed network path: The machines have no public address and accept no inbound connection at all. Outbound traffic is limited to a named list of internal destinations, and each disk is encrypted under a dedicated key.
  • No wildcard permissions: Every permission the machines hold names the exact resource it covers, so nothing is granted by pattern.
  • PII masking in logs: A structured JSON logger reduces phone numbers to their last four digits and replaces email addresses before any line reaches the journal.
Security

Per-Contact Consent Registry for Outbound Calling

Consent for every contact is stored where it cannot be quietly deleted, so you can always show who agreed to be called and when.

Added a durable consent record for every contact the system may call, stored encrypted and protected against deletion, and locked the calling host out of the credential store at the same time.

  • A consent record per contact: Consent is held in a dedicated store keyed by contact, encrypted with a customer-managed key, with point-in-time recovery, an expiry attribute, deletion protection, and a lifecycle rule that refuses to destroy the store even if a future change asks it to.
  • Read and write access only where needed: The calling host is granted item-level reads, writes, and lookups against that store and nothing more.
  • Credential store closed off: The broad managed policy on the calling host was replaced with a narrow one covering only the remote session channels it needs, plus an explicit denial of every credential store read and write. The denial is what makes it durable, because a later policy cannot grant the access back.
  • Outbound work queued, not called directly: The host was given permission to place jobs on the outbound queue, keeping the request path and the calling path separate.

February 2026

New

Interactive Weather System with Three-Tier Expandable Surface

Tapping the weather in your calendar opens the forecast, so you can plan a week of outdoor jobs without leaving the dashboard.

Overview

Rebuilt the calendar weather pill as a standalone widget with three interaction tiers, and added the forecast API route that feeds the two new ones.

Architecture & Key Capabilities

  • Three interaction tiers: The pill shows temperature, condition, humidity, and wind with condition-driven icon colors; hovering for 200ms opens a three-day preview tooltip with highs, lows, and precipitation chance; clicking opens the full panel.
  • Draggable forecast panel: The panel renders through a portal, moves under native pointer events, keeps its position clamped inside the viewport, and suppresses text selection while a drag is in progress. Its body carries a scrollable hourly strip for the rest of the day and a five-day forecast with low-to-high temperature bars.
  • Forecast API route: A new route aggregates the OpenWeatherMap five-day, three-hour feed into daily highs, lows, condition, precipitation chance, humidity, and wind, plus today's remaining hourly entries. It resolves location from coordinates, a ZIP, or a ZIP parsed out of the business address, and caches each result in process for 30 minutes.
  • Lazy forecast fetch and extraction: Forecast data loads only on the first hover or click rather than with the calendar, and the weather state moved out of the calendar component into the new widget.
Faster

The iPhone App Updates Live Instead of Every Forty-Five Seconds

The iPhone app shows a new call the moment it happens, rather than up to forty-five seconds later.

Overview

Replaced the iOS dashboard's forty-five second refresh timer with a live connection, so calls, bookings, usage, workspace changes, agent configuration and notifications appear the moment they happen.

Architecture & Key Capabilities

  • Live connection at startup: The app opens its live connection as it launches, authorizing with the session already stored on the device rather than asking you to sign in a second time.
  • Six subscription channels: A realtime manager owns the subscription lifecycle for calls, bookings, usage, workspace, agent configuration, and notification events, decoding each into a typed model and dispatching it through NotificationCenter to the dashboard, calls, and bookings view models.
  • Polling timer removed: The dashboard's 45-second refresh timer is gone. The app reconnects its subscriptions when it returns to the foreground, issues one REST refresh at that point, and disconnects them when it goes to the background.
  • Connection tied to auth state: Subscriptions connect on successful sign-in and disconnect on sign-out.
New

iOS Client UI Modernization with Glassmorphism and Design System

The iPhone app reads like the web dashboard, so moving between them does not feel like using two products.

Overview

Reworked the iOS theme layer against the web palette and rebuilt the login and calendar surfaces on top of it.

Architecture & Key Capabilities

  • Token layer matched to the web palette: The iOS theme restates the purple scale, slate neutrals, and status colors against the values in the web stylesheet, and adds semantic tokens for raised and inset surfaces, secondary and muted text, and two border weights across light and dark.
  • Parameterized glass and shadow modifiers: The glass modifier now takes a material intensity and draws a gradient stroke instead of a flat one, and the shadow modifier gained an optional brand glow variant.
  • Login screen rebuild: The sign-in view moved to a glass form container with a sign in and sign up segmented toggle, icon-prefixed text fields, larger heading type, a brand glow on the logo, and error text drawn from the token set.
  • Calendar surfaces on tokens: The calendar header and view mode picker read their colors from the token set, the atmospheric glow now varies by color scheme, and the picker was fixed against label wrapping and given an inset background with a subtle border.
Fixed

Seconds-Based Usage Billing Engine with Precise Rounded Displays

Your minutes used reads as a clean number instead of a long decimal fraction.

Overview

Rounded the minutes figure shown in the dashboard billing panel. The API returns decimal minutes derived from exact seconds, which rendered as a long fraction in the console.

Architecture & Key Capabilities

  • Rounded minutes display: The billing panel rounds the decimal minutes value from the billing summary to the nearest whole minute before rendering the usage figure and the progress gauge against the plan cap.
  • Scope of the change: This is a single display call site in the dashboard. Metering and the amount billed are untouched by it.

January 2026

New

Desktop Integrations Hub for Centralized Service Management

You can see whether a connected tool is syncing, force a sync, change what it syncs, and disconnect it, all from one place.

Overview

Added integration management to the desktop dashboard: a connected integration card now opens a detail dialog covering account status, sync state, sync settings, and disconnect.

Architecture & Key Capabilities

  • Integration detail dialog: A new desktop dialog reports connection status, the connected account name and ID, and the date the integration was connected.
  • Dashboard card wiring: Connected cards became clickable, show the connected account name in place of the static description, and label their action "Manage Integration"; cards for providers without a connection stay inert.
  • Sync status and manual trigger: The dialog shows the last sync time behind a status badge for synced, syncing, error, and never-synced states, surfaces the recorded sync error when the last attempt failed, and offers a button that requests a sync on demand.
  • Sync settings toggles: An expandable panel switches call log sync, booking sync, and automatic contact creation, saving each toggle as it is changed.
  • Guarded disconnect: Disconnecting requires an inline confirmation step before the request is sent, and the dialog closes once it succeeds.
New

Integration Configuration Slide-Over Sheet with Lifecycle Controls

The same connection controls you get on a computer are available on a phone.

Overview

Built the mobile integration detail sheet and the hook actions behind it, so a connected provider can be inspected, resynced, reconfigured, and disconnected from the Integrations tab.

Architecture & Key Capabilities

  • Mobile detail sheet: Tapping a connected integration opens a full-height sheet that slides up over the tab, showing connection status, account name and ID, last sync time with a status badge, and the recorded sync error when the last attempt failed.
  • Lifecycle actions in the hook: The integrations hook gained disconnect, settings update, and manual sync calls against the integrations API, each reporting success or failure through a toast and refreshing provider status afterward.
  • Richer provider status: Provider records now carry connected-at, account name and ID, last sync time, sync status, sync error, and per-integration settings, merged into the catalog entries the UI renders.
  • Sync settings and disconnect: An expandable panel toggles call log sync, booking sync, and automatic contact creation; disconnect sits behind an inline confirmation before the request is sent.
New

Mobile Dashboard Navigation and Tab Interaction Overhaul

Editing services and FAQs on a phone is where you would expect to find it, and appointments read as a timeline instead of a list.

Overview

Reorganized the mobile dashboard tabs: services and FAQ editing moved from the Agent tab to the Business tab, the Appointments tab was rebuilt as a timeline, and the call outcome labels were corrected.

Architecture & Key Capabilities

  • Agent tab narrowed: The service item and FAQ editors, and the expanding input control they shared, were removed from the Agent tab, leaving prompt viewing, editing and regeneration, plus copy pointing at the Business tab and a jump link to the prompt section.
  • Business tab owns services and FAQs: The Business tab picked up service category and FAQ editing driven by the industry form schema, including name suggestions, and now saves the business and agent configuration together behind a combined dirty check.
  • Appointments timeline: The day list was rebuilt around a vertical rail with a status colored dot per event, past events dimmed, a date chip and event count in the header, and reworked loading, error and empty states.
  • Call outcome labels: The label helper gained ongoing, completed, transferred and hungUp cases and now takes the call duration, so a call still marked ongoing while carrying a duration reads as Completed in both the label and the list icon.
Fixed

OAuth Window Popup Authentication Flow for Third-Party Integrations

Connecting an outside tool completes in one window instead of failing partway through with nothing to show for it.

Overview

Moved the integrations OAuth popup onto first-party consent and callback pages, so the authorization start and completion calls are made from a window that carries the session cookie.

Architecture & Key Capabilities

  • First-party consent page: A new per-provider page under /connect renders the EarlyBird and provider logos, resolves the provider from the integrations catalog, and on continue calls the integration start endpoint and sends the popup on to the provider's authorization URL.
  • Callback page: A new /connect/callback page reads the code and state, or the provider's error parameters, from the query string, posts them to the integration complete endpoint, and shows a processing, success or error state.
  • Completion handshake: The callback posts a completion message to its opener, targeted at the app's own origin, and the integrations hook checks the message origin before clearing the connecting state, refreshing provider status and raising a toast.
  • Popup lifecycle: The hook also polls for a popup the user closed without completing, and tears down its listener and interval after ten minutes.

December 2025

Security

SMS Compliance Consent Modal for Carrier and Telephony Verification

Text notifications cannot be switched on without your consent on record, which is what carriers require before they will deliver your messages.

Added an SMS opt-in consent modal to the account settings notification toggles, so SMS notifications cannot be switched on without an explicit acknowledgment, as required for Twilio toll-free verification.

  • Consent gate on the toggles: Turning on the SMS channel for booking, missed call or billing alerts opens the modal when no consent has been recorded. The toggle only flips once consent succeeds, and the stored consent state is read from the settings payload on load.
  • Required disclosure copy: The modal states that message frequency varies with notification settings, that message and data rates may apply, and that replying STOP unsubscribes and HELP gets assistance. The agreement checkbox restates the STOP instruction alongside it.
  • Consent submission: Submitting posts the agreement flag, the target phone number and an ISO 8601 consent timestamp with a CSRF token, then shows a success state before closing. A failure surfaces the server message inline instead.
  • Dialog handling: The modal masks the middle digits of the phone number, offers a jump to the profile tab to edit it, focuses the consent checkbox on open, closes on Escape or a backdrop click, carries dialog and aria-modal semantics, and links out to the privacy policy and terms of service.
New

Apple-Style Light Mode Theming with Seamless Dark Mode Parity

Light mode is readable in daylight, with real contrast rather than a dark theme with its colors inverted.

Overview

Restyled the dashboard light mode toward an Apple-like palette of off-white page background, near-black text, hairline borders and soft shadows, leaving the existing dark mode variants in place.

Architecture & Key Capabilities

  • Light palette swap: The dashboard shell moved to an F5F5F7 page background with 1d1d1f body and heading text in place of the previous slate-50 and slate-900 utilities, and the dashboard layout wrapper dropped its own background and text classes so the dashboard component owns the surface.
  • Hairline borders and soft shadows: Slate border utilities were replaced with black hairlines at 4 percent opacity, cards moved from the shared shadow utility to explicit low-opacity drop shadows and a 24px corner radius, and the sidebar and sticky header keep translucent white backgrounds over a backdrop blur.
  • Surfaces covered: The same treatment reaches the stat cards, the sidebar and its account row, the sticky header, the calls table header, the plan, provisioning and support cards, and the booking and cancel confirmation modals.
  • Dark mode preserved: Every dark variant class was carried through unchanged, and the calls list row divider gained a light mode color so rows stay visible in both themes.
New

Real-Time Weather Intelligence Widget for Desktop Calendar

Your calendar shows the real conditions at your business address, which matters when you are deciding whether to confirm an outdoor job.

Overview

Put live current conditions for your business address into the desktop calendar header, where a fixed graphic used to sit.

Architecture & Key Capabilities

  • Weather API route: A new server route queries the OpenWeatherMap current conditions endpoint and returns temperature, condition, icon code, wind speed and humidity in imperial units.
  • Location resolution: The route accepts an address, a US zip, or a latitude and longitude pair. For an address it uses an embedded five digit zip when one is present and otherwise geocodes the address through the OpenWeatherMap geocoding endpoint.
  • Server-side cache: Responses are held in an in-process map for 30 minutes, keyed by the lookup value, and each response carries a header marking whether it was a cache hit or a miss.
  • Calendar header widget: The widget reads the business address from the shared business config hook, picks its icon from the returned condition string (rain, snow, thunder, fog, cloud or clear), renders a loading state, and hides itself when no address is set or no reading came back.
New

Mobile Visitors Routed to the Mobile Site

Visiting on a phone lands you on the mobile dashboard automatically, with no separate link to remember.

Overview

Requests arriving on the mobile address are now sent to the mobile pages automatically. Pages already under the mobile path and the API surface are both excluded, so nothing loops back through the rule.

Architecture & Key Capabilities

  • Hostname match: The middleware resolves the request host from the x-forwarded-host header, falling back to the host header and then the request URL hostname, and lowercases it before testing for the m. prefix.
  • Path prefixing: A matching request is redirected with /m prepended to its pathname, and a request for the site root is sent to /m.
  • Redirect exclusions: Paths already under /m and any path under /api are left alone, so the shell routes and the API surface do not loop back through the redirect.

November 2025

New

Dashboard V2 Console Release with Modular Workspace Overview

Moving between calls, appointments, billing and settings no longer reloads the page, so the dashboard answers immediately.

Overview

Replaced the dashboard home page with a single console component that switches between six views in place, and let that one route bypass the shared dashboard chrome.

Architecture & Key Capabilities

  • Single console component: The dashboard route now renders one client component that carries its own sidebar, header, and view state. Selecting Dashboard, Calls and Recordings, Appointments, Integrations, Billing and Plan, or Settings switches an active tab without a route change, and the calls and appointments entries show a count badge. The console sidebar is hidden below the medium breakpoint.
  • Layout bypass: The dashboard layout detects the dashboard path and returns the console inside a bare shell, skipping the shared sidebar and header that still wrap every other dashboard route.
  • Overview metrics: Three stat cards report total calls over the last seven days, minutes used against the plan cap with a progress bar, and appointments booked with a count of upcoming bookings. The metric grid runs one column on small screens and three from the medium breakpoint up.
  • Live activity and calendar: A live activity panel lists the four most recent calls with caller, outcome, duration, and timestamp, pulled from the calls list endpoint. A week calendar renders bookings from the bookings list endpoint, and a modal creates a booking through the bookings create endpoint before reloading the list.
New

Setup Progress Tracking Engine with Dynamic Step Routing

The dashboard tells you how much of setup is done and takes you straight to the next thing that still needs doing.

Overview

Replaced the dashboard's static account setup banner with a three-step progress banner that reports how much of setup is done and sends the operator to the first step still outstanding.

Architecture & Key Capabilities

  • Three tracked steps: Account, Plan, and Business Profile. Account resolves from the loaded profile record, Plan from a subscription summary status other than none, and Business Profile from the completion flag or, failing that, from a business name and a normalized address both being present.
  • Progress readout: The banner shows a rounded completion percentage, a row of dots colored per step, a completed-of-total step count, and a line of copy specific to the first incomplete step.
  • Routing to the next step: The banner button targets whichever step is first incomplete. Account and Plan navigate to the account creation path and to pricing; Business Profile opens the setup wizard in place rather than navigating away.
  • Server state over the session flag: The business setup check no longer keys off the session's account-create flag, which could be stale, and derives account state from the fetched profile instead. Each step also reports an undecided state while its data is loading, so the banner stays hidden until both requests settle.
New

Responsive Dashboard Navigation with Sticky Sidebar and Mobile Nav

The dashboard menu stays put while you scroll on a computer, and moves to a thumb-reachable bar on a phone.

Overview

Reworked the dashboard layout navigation so the desktop sidebar stays in place while content scrolls, and small screens get a fixed bottom navigation bar.

Architecture & Key Capabilities

  • Sticky desktop sidebar: The sidebar is now positioned sticky at the top of the viewport at full viewport height, so it holds its position while the main column scrolls.
  • Fixed mobile bottom bar: A bottom bar, hidden from the medium breakpoint up, renders each destination as an icon above its label in a horizontally scrollable row. It adds padding for the device safe area inset, and the page wrapper gained bottom padding on small screens so content clears the bar.
  • Shared destination list: Both navigations render from one array of seven destinations: Dashboard, Calls and Recordings, Appointments, Integrations, Phone and Agent, Billing and Plan, and Settings.
  • Inline icon set: Seven inline SVG icons were added, one per destination, drawn as stroked paths that inherit the current text color.
  • Active route indication: The current destination is marked with aria-current, a brighter text color, and, in the mobile bar, a short underline pill.
Fixed

Direct Magic Link Verification Protocol Without OTP Prompts

A sign-in link from your email signs you straight in, instead of stopping to ask for a code you were never sent.

Routed email magic link tokens to a dedicated verification endpoint so the link no longer falls through the one-time password verify path.

  • Token branch in the verify helper: The shared verify function now checks whether the payload carries a token and, if it does, runs the magic link path and returns immediately instead of posting to the OTP verify endpoint.
  • Dedicated verify call: A new helper posts the token to the auth magic verify endpoint and treats only a 200 response as success.
  • CSRF token forwarded: The request carries the already-fetched CSRF token as an x-csrf-token header whenever one is available.
  • Error code surfaced: A non-200 response throws with the error code from the response body when the body supplies one, and otherwise with a code derived from the HTTP status, so callers can distinguish failure modes.
Security

Host-Bound Authentication Flow for Secure Origin Delegation

Signing in with Google finishes on your own dashboard address, so your session is never handed across an origin you did not start from.

Overview

Moved Google sign-in off a client-built authorization URL and onto a single backend start-auth entry point, and replaced the per-intent Google callback routes with one shared callback page on the app's own origin.

Architecture & Key Capabilities

  • Backend start-auth entry point: A new client helper opens the API start-auth endpoint carrying only an intent and a mode parameter. The client-side builder that assembled the Google authorization URL and passed its own return host and return path was deleted, so the host is no longer chosen in the browser.
  • Popup with redirect fallback: Sign-in opens in a popup and falls back to a full page navigation when the popup is blocked. A launch failure now clears the pending state and shows an inline error, on the login page and in the auth modal alike.
  • Single callback route: One callback view replaced the per-intent Google callback pages on both the desktop and mobile paths. It finalizes the session, then either posts a success message to the opener and closes itself, or redirects in place when it is not a popup.
  • Constrained redirect parameter: The callback honors the redirect query parameter only when it begins with a slash, and otherwise falls back to the dashboard or the account pending path. The API origin was dropped from the auth modal's accepted message origins now that the callback runs on the app's origin.
  • Request correlation id: Every API call now carries an x-run-id header, generated once per page load and reused for the life of that load.

October 2025

New

Business Setup Onboarding Wizard with Active Plan Status Gating

Setup opens only once your plan is active, so you are never asked to configure a business you cannot use yet.

Overview

Gated the existing business setup wizard so it only opens for accounts whose plan status is an active trial or an active paid plan.

Architecture & Key Capabilities

  • Plan status allowlist: A set of two accepted statuses, trial-active and active, now guards the check that decides whether business setup is still outstanding. Any other status, including none, suppresses the wizard.
  • Applied on every surface: The same gate was added to the desktop dashboard, the mobile dashboard, and the settings page, so the wizard cannot open from any of them without an eligible plan.
  • Waits for the plan summary: The two dashboards now treat the setup check as undecided while the plan summary request is still in flight, so the wizard no longer opens against a plan status that has not loaded yet.
Security

Per-Request Script Nonces and a Locked-Down Content Policy

Every page load carries a policy that permits only the scripts your dashboard actually shipped, which is what stops an injected script from running.

Overview

Rebuilt the request layer around a content security policy that mints a fresh single-use value for every page load and names it as the only script permitted to run, which is what stops an injected script from executing at all. The policy shipped in reporting mode first so real violations could be observed before anything was blocked, and a build check now refuses any inline script added to the codebase.

Architecture & Key Capabilities

  • Per-request nonce: The middleware draws 16 random bytes from the Web Crypto API, base64 encodes them, passes the value downstream on an x-nonce request header, and names it in the script-src directive. Style rules still permit inline styles rather than carrying the nonce.
  • A pinned directive set: The policy pins where content, page bases, embedded objects, framing and form targets may come from, and names the small set of outside origins allowed to serve fonts and payment scripts. Cross-origin isolation headers are set alongside it.
  • Inline script CI gate: A new policy script, wired ahead of eslint in the lint command, fails the build when any source file under the app, components, or pages directories contains a script tag or dangerouslySetInnerHTML, and when a lockfile is found outside the repo root.
  • An analytics embed removed: A third-party analytics script was taken out of every page and its dependency dropped, so one fewer outside origin runs in your browser.
New

Plan Checkout and Trial Call-to-Action Buttons

You can start a trial or buy a plan from the pricing page or the billing screen, with no sales call in between.

Overview

Added trial and purchase call-to-action buttons to the pricing page and the billing page, and reworked the plan catalog so every plan carries its Stripe price id.

Architecture & Key Capabilities

  • Plan checkout buttons: A new client component renders a Start Free Trial button and a Purchase button for each plan. Each one posts the plan price id to the matching billing endpoint, /billing/trial/start or /billing/checkout/start, and sends the browser to the URL the response returns. A failure is shown inline using the message the server returned.
  • Plan catalog rewrite: The plan module dropped the minute-bundle shape and now describes each plan with a price label, feature list, and Stripe price id, plus lookup maps keyed by price id and by slug.
  • Status-driven button state: The trial button is disabled when the reported plan status is trial-active, active, or trialing, and the purchase button is disabled for the plan the account is already on. Both pages resolve that status and the active plan from the Stripe usage response.
New

One Sign-In Path for Passwords and Google

Signing in with Google works the same way as signing in with a password, and staying signed in survives a page reload.

Overview

Moved every authentication path off a third-party session library and onto our own service. Sign-in, Google sign-in, session detection, and sign-out now all run through backend routes and the cookies they set. The identity provider integration itself sits behind that backend and is not part of this change.

Architecture & Key Capabilities

  • Third-party session library removed: The session library and every reference to it were deleted, including the provider-discovery request the login page used to make before it could render.
  • Backend-backed sign-in: The login form posts credentials to the backend login route with cookies included, mapping a 400 or 401 to a failed-login message and any other failure to unavailable. The Google button now sends the browser to the backend OAuth start route and is always rendered rather than gated on a fetched provider list.
  • Cookie-based session check: The page decides whether a session exists by reading our own session cookies. The header's account dropdown was replaced with a direct dashboard link and a sign-out button that posts to the backend logout route before returning to the home page.
  • Covered by tests: An automated suite for the sign-in client landed with this change, alongside end-to-end scripts that exercise the full flow in a browser.
New

Email and Password Sign-Up with Dashboard Auto-Login

You can create an account with an email and a password and land straight in the dashboard, instead of waiting on an invitation.

Overview

Enabled the email and password sign-up form, which until this change rendered with a disabled button. The form now creates the account against the backend, signs in immediately so the backend can set its session cookies, and redirects the browser to the dashboard.

Architecture & Key Capabilities

  • Working sign-up submit: The create-account button had been disabled and unavailable. It now submits the form, and the email, password, and confirm-password fields are bound to component state.
  • Client-side validation: Submission is rejected before any network call when a required field is empty or the password and its confirmation do not match, and the resulting error code is shown beneath the form.
  • Create then sign in: On submit the form posts to the backend sign-up route, treating an already-exists response as success, then posts to the login route with credentials included so the backend sets its cookies, then redirects to the dashboard. The request base comes from the configured API base, falling back to a proxy path when that is unset.
  • Style parity with sign-in: The enabled submit button reuses the sign-in button's styling and gains a busy state that disables it and swaps the label while the request is in flight.

September 2025

Security

Edge Middleware Rate Limiting and Hardened Security Headers

A flood of automated requests against one business cannot slow the service down for everyone else on it.

Overview

Added a per-IP request limit for API routes to the edge middleware, extended the response header set, and scoped the middleware away from static assets.

Architecture & Key Capabilities

  • Per-IP request limit: Requests to paths under the API prefix are counted per client IP in an in-memory map over a fixed 60-second window and capped at 120 requests. The client IP is taken from the request IP, then the first x-forwarded-for entry, then a fallback value.
  • Throttle response: Past the cap the middleware answers 429 with a Retry-After header of 60 seconds plus the rate limit and remaining-count headers, without invoking the route handler.
  • Response headers: Other responses carry a referrer policy, content-type and framing protections, and a permissions policy denying ten browser features outright.
  • Middleware matcher: A matcher was added so the request layer skips static assets and images, the favicon, robots.txt, and sitemap.xml.
New

Pricing Page with Starter, Growth, and Scale Tiers

Anyone comparing plans can see what Starter, Growth and Scale each include and start from the tier that fits, without a sales call.

Overview

Rebuilt the public pricing page as a three-card plan layout covering the Starter, Growth, and Scale tiers, each card carrying its own feature list and sign-up link.

Architecture & Key Capabilities

  • Three plan cards: Starter at $39 per month, Growth at $149 per month, and Scale priced on request, laid out as a responsive grid that collapses to a single column on narrow viewports.
  • Per-plan feature lists: Each card lists its included phone numbers, included minutes, and analytics level. The Scale card lists unlimited numbers, volume rates, and SSO, SAML, and audit logs.
  • Sign-up entry points: Every card ends in a call-to-action link pointing at the sign-up route, with the label varied per tier.
  • Billing footnote: A closing note states that overages are billed at standard per-minute rates and that taxes or VAT may apply.

Signing up is easy.