Compare commits

...

104 Commits

Author SHA1 Message Date
Chris Weaver
186ce13c24 Fix web again (#7780) 2026-01-25 19:25:52 -08:00
roshan
2c16436a1b feat: DEMO DATA SNEAKY LINK (hookup) (#7779) 2026-01-25 18:57:21 -08:00
Wenxi
0ba23c4465 update rec models list and fix onboarding modal ai slop (#7778) 2026-01-25 18:41:54 -08:00
Chris Weaver
b2fb6a31d0 Kube web server (#7777) 2026-01-25 18:34:56 -08:00
roshan
2250a83f35 demo data toggle (#7775) 2026-01-25 18:16:21 -08:00
Wenxi
3c3750a922 completely overhaul pre-provisioning with llm selection and demo data toggle (#7776) 2026-01-25 18:01:00 -08:00
Chris Weaver
cc80712533 more kube stuff (#7774) 2026-01-25 16:25:32 -08:00
roshan
1f544875e3 feat: FILE UPLOAD (#7772) 2026-01-25 15:59:16 -08:00
Chris Weaver
52bfe467e3 Kube testing (#7771) 2026-01-25 14:40:57 -08:00
Wenxi
1c5ab38a31 demo data card styling, coming soon connectors, submit connector request, onyx craft intro (#7770) 2026-01-25 14:10:45 -08:00
roshan
4ba8e38988 user identity profile mapping for kube sandboxes (#7766) 2026-01-25 13:52:21 -08:00
rohoswagger
db603e8b6f hoepfully no more black charts on dark mode 2026-01-25 13:21:51 -08:00
roshan
9631e1aec4 local sandbox demo data hookup (#7765) 2026-01-25 10:45:23 -08:00
Wenxi
7ca15aac82 fix layout shifting in chat cards (#7764) 2026-01-25 10:31:19 -08:00
Wenxi
70708e90c5 init onboarding visuals (#7763) 2026-01-25 10:20:57 -08:00
Wenxi
9e72970b78 refactor: show fewer tool call components (#7755) 2026-01-24 17:27:31 -08:00
Wenxi
cd53ffdfd6 open webapp in new tab, allow links in iframe, output panel controls, auto scroll (#7750) 2026-01-24 16:29:31 -08:00
Chris Weaver
74a0bb3a8f Kubernetes cont (#7749) 2026-01-24 15:26:06 -08:00
Wenxi
76a6d7d597 fix: gdrive actually access folder ids, troll docfetching concurrency hard code, gmail xtrm troll bug (#7748) 2026-01-24 14:57:17 -08:00
Wenxi
153706cdf5 fix chat rendering and tab curve (#7741) 2026-01-23 20:35:00 -08:00
Wenxi
6bd2ffb99c fix not allwoed modal, allow managing all build connectors, render assistant messages correctly (#7740) 2026-01-23 19:05:45 -08:00
Chris Weaver
e447c73e85 Single sandbox (#7728) 2026-01-23 17:04:58 -08:00
Wenxi
6819d5359a whuang/cc4a nits 2 (#7729) 2026-01-23 16:32:20 -08:00
rohoswagger
58d01c6e59 nit 2026-01-23 16:31:21 -08:00
rohoswagger
f87b794fb5 agents.md generation nit 2026-01-23 16:30:30 -08:00
roshan
40ff76d32b consolidate cookies and send to BE (#7725) 2026-01-23 16:11:07 -08:00
Wenxi
4e9f693a6e fix build connectors (#7726) 2026-01-23 16:09:01 -08:00
rohoswagger
8ebdeaba69 output panel nit 2026-01-23 15:54:56 -08:00
Wenxi
4072b6dd20 feat: init onboarding happy path (#7724) 2026-01-23 15:50:40 -08:00
rohoswagger
c1c4fa299a png preview nits 2026-01-23 15:39:36 -08:00
roshan
75edbc6a71 PNG RENDERING (#7723) 2026-01-23 15:29:41 -08:00
roshan
95cf65031c feat: web template in /build, better sandbox provisioning (#7715) 2026-01-23 15:19:54 -08:00
roshan
21e4100276 feat: dynamic AGENTS.md creation in kubernetes (#7704) 2026-01-23 14:44:27 -08:00
Wenxi
7dacdf083a chore: delete unused files and v1_api (#7705) 2026-01-23 11:43:41 -08:00
roshan
bb79a67fc8 feat: dynamic AGENTS.md creation (#7694) 2026-01-23 10:48:52 -08:00
Wenxi Onyx
3f8cefaa68 rebase migrations on main head 2026-01-23 10:27:10 -08:00
Wenxi Onyx
3c64b4507f Merge remote-tracking branch 'origin/claude-code-for-all' into claude-code-for-all 2026-01-23 10:17:08 -08:00
Wenxi Onyx
744c28dadd Merge main into claude-code-for-all 2026-01-23 10:14:42 -08:00
Chris Weaver
a175a5c393 Kube deploy v2 (#7695) 2026-01-23 10:05:29 -08:00
Wenxi
fac0ec03b9 fix reenable demo data on connector deletion (#7693) 2026-01-22 20:36:10 -08:00
Wenxi
4ce69fc885 fix dramatic intro, init suggested prompts, unique connector names (#7692) 2026-01-22 20:28:09 -08:00
rohoswagger
0190401c63 external directory allow -> sus 2026-01-22 19:20:49 -08:00
Wenxi
12bb2e3c51 fix session naming and output panel auto-opening (#7690) 2026-01-22 19:05:18 -08:00
Wenxi
79d6f9167e whuang/fix ssr hydration cc4a (#7687) 2026-01-22 18:13:58 -08:00
Wenxi
0fd082bcca feat: IDEAL OUTPUT PANEL (#7686) 2026-01-22 17:42:46 -08:00
Chris Weaver
fbcf4b452e Kubernetes deployment (#7685) 2026-01-22 17:41:06 -08:00
rohoswagger
4f1bdf1de1 add more curl and wget commands 2026-01-22 17:28:34 -08:00
roshan
96345db5a7 THINKING PACKETS :BIG_GRIN: (#7680)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-01-22 16:04:15 -08:00
Wenxi
8562ab3466 feat: RENDER ALL PACKET TYPES :BIG_GRIN: (except thinking) (#7678) 2026-01-22 13:11:31 -08:00
roshan
84dffa0414 fix nextjs port dying (#7675) 2026-01-22 11:05:09 -08:00
rohoswagger
8ae896847d fix chat panel with video bg 2026-01-22 10:41:04 -08:00
roshan
2d4d00b506 backend packet fixes (#7667) 2026-01-22 09:06:55 -08:00
Wenxi
c8057e1257 feat: build chat panel rendering (#7666) 2026-01-21 20:33:01 -08:00
roshan
5f641dac35 connectors page (#7664) 2026-01-21 19:54:27 -08:00
roshan
ff101313b6 video bg + chat panel beautification (#7663) 2026-01-21 19:08:39 -08:00
roshan
5bf35f3c36 atomic session operations - creation and deletion (#7651)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-01-21 18:31:51 -08:00
Wenxi
ba8beb40d4 dmeo data switch frontend (#7650) 2026-01-21 15:50:56 -08:00
roshan
1115e515a8 build page uses standard app layout (#7649) 2026-01-21 15:19:46 -08:00
roshan
7063fa354b thorough packet audit (#7643) 2026-01-21 14:10:08 -08:00
Wenxi
d3f19294db fix: race condition? (#7645) 2026-01-21 14:08:53 -08:00
Wenxi
3df1cb0759 feat: pre-provision sandboxes (#7633) 2026-01-21 13:39:50 -08:00
Chris Weaver
06c561b3fd . (#7641) 2026-01-21 13:22:23 -08:00
Wenxi
7358bb4bc2 mypy (#7635) 2026-01-21 11:58:45 -08:00
roshan
a16e65d5cb video background (#7631) 2026-01-21 10:42:21 -08:00
Wenxi
0cd4ad921e feat: file uplod (#7630) 2026-01-21 10:30:37 -08:00
roshan
28194a58b5 very very crude fe implementation (#7617) 2026-01-20 19:58:20 -08:00
Chris Weaver
ed6e134b28 Sandbox cleanup (#7615) 2026-01-20 19:11:38 -08:00
roshan
2f2d65a950 chore: be fixes + improvements (#7613)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-01-20 19:01:08 -08:00
Chris Weaver
2d436fb207 Better connector permissioning (#7612) 2026-01-20 18:50:49 -08:00
Chris Weaver
c0d9c17312 Fix web template (#7610) 2026-01-20 18:12:57 -08:00
Chris Weaver
5462460ae7 Update connectors to include sandbox only mode (#7599) 2026-01-20 17:28:31 -08:00
roshan
fa44774ef3 chore: big back be cleanup (#7602) 2026-01-20 17:04:58 -08:00
Wenxi
e2bd32e405 feat: build admin panel (#7596) 2026-01-20 15:21:21 -08:00
rohoswagger
74431ea0a8 port over send message 2026-01-20 14:13:42 -08:00
rohoswagger
9ed9c95007 rate limit send message endpoint 2026-01-20 14:13:02 -08:00
roshan
b06cf041d7 feat: rate limiting for onyx build (#7556) 2026-01-20 14:11:05 -08:00
Chris Weaver
fe2cc230b5 improve template set up (#7591) 2026-01-20 14:06:26 -08:00
Weves
544bcf8e7f Merge branch 'main' into claude-code-for-all 2026-01-20 14:00:05 -08:00
Chris Weaver
b818709b7d Fix ports (#7580) 2026-01-20 11:26:40 -08:00
Wenxi
3ccf71c5ee whuang/cc4a fe cont (#7554) 2026-01-19 20:10:01 -08:00
roshan
e8cac79b96 messages apis + migrations (#7553) 2026-01-19 20:08:21 -08:00
Wenxi
81967d3507 mock router (#7552) 2026-01-19 19:16:56 -08:00
Wenxi
6c5197cb74 mock backend (#7551) 2026-01-19 19:14:07 -08:00
Wenxi
6d3e4809e3 feat: fe draft (#7549) 2026-01-19 19:10:03 -08:00
Chris Weaver
f61a275aa5 feat: basic sandbox implementation (#7523)
Co-authored-by: Wenxi <wenxi@onyx.app>
2026-01-19 18:42:20 -08:00
roshan
3715d1ed75 feat(cc4a): session + sandbox endpoints (#7545)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-01-19 17:16:41 -08:00
roshan
6e3fa6fbac basic build models + migrations (#7542)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-01-19 16:11:19 -08:00
Chris Weaver
537bdf75a9 Improve plan (#7522) 2026-01-19 15:34:37 -08:00
Wenxi
f706a411f1 tiny reorg of fe files (#7516) 2026-01-19 15:34:37 -08:00
Wenxi
e54c7c377a feat: dramatic intro animation + framer dep (#7512) 2026-01-19 15:34:37 -08:00
Weves
9371cbecb4 High level overview plan 2026-01-19 15:34:37 -08:00
Wenxi
1ae7e4feee feat: init configs file and move simple_cli_client configs (#7507) 2026-01-19 15:34:37 -08:00
Weves
2649876f64 . 2026-01-19 15:34:37 -08:00
Weves
4628b711be . 2026-01-19 15:34:37 -08:00
Weves
1dd8e07310 Add file viewer 2026-01-19 15:34:37 -08:00
Weves
295de1268e Add slide autogeneration 2026-01-19 15:34:37 -08:00
Weves
f8ef7d5ea5 more 2026-01-19 15:34:37 -08:00
Weves
343cd5cb1b improvements 2026-01-19 15:34:37 -08:00
Weves
08228e1a10 improve 2026-01-19 15:34:37 -08:00
Weves
95df31aa01 Basic implementation 2026-01-19 15:34:37 -08:00
Weves
ba45f9bc77 . 2026-01-19 15:34:37 -08:00
Weves
be088a0964 . 2026-01-19 15:34:37 -08:00
Weves
67bb013063 add more connectors + initial /build endpoints 2026-01-19 15:34:37 -08:00
Weves
9140c4c449 Open Cowork 2026-01-19 15:34:37 -08:00
1762 changed files with 134571 additions and 106 deletions

View File

@@ -66,7 +66,8 @@ repos:
- id: uv-run
name: Check lazy imports
args: ["--active", "--with=onyx-devtools", "ods", "check-lazy-imports"]
files: ^backend/(?!\.venv/).*\.py$
pass_filenames: true
files: ^backend/(?!\.venv/|scripts/).*\.py$
# NOTE: This takes ~6s on a single, large module which is prohibitively slow.
# - id: uv-run
# name: mypy

19
.vscode/launch.json vendored
View File

@@ -397,7 +397,6 @@
"onyx.background.celery.versioned_apps.docfetching",
"worker",
"--pool=threads",
"--concurrency=1",
"--prefetch-multiplier=1",
"--loglevel=INFO",
"--hostname=docfetching@%n",
@@ -428,7 +427,6 @@
"onyx.background.celery.versioned_apps.docprocessing",
"worker",
"--pool=threads",
"--concurrency=6",
"--prefetch-multiplier=1",
"--loglevel=INFO",
"--hostname=docprocessing@%n",
@@ -577,6 +575,23 @@
"group": "3"
}
},
{
"name": "Build Sandbox Templates",
"type": "debugpy",
"request": "launch",
"module": "onyx.server.features.build.sandbox.build_templates",
"cwd": "${workspaceFolder}/backend",
"envFile": "${workspaceFolder}/.vscode/.env",
"env": {
"PYTHONUNBUFFERED": "1",
"PYTHONPATH": "."
},
"console": "integratedTerminal",
"presentation": {
"group": "3"
},
"consoleTitle": "Build Sandbox Templates"
},
{
// Dummy entry used to label the group
"name": "--- Database ---",

91
BUILD_CONNECTOR_AUDIT.md Normal file
View File

@@ -0,0 +1,91 @@
# Build Mode Connector Audit Report
## Connectors in Build Mode
1. GoogleDrive
2. Gmail
3. Notion
4. GitHub
5. Slack
6. Linear
7. Fireflies
8. Hubspot
## Issues Found
### Issue 1: Missing `groups` parameter in connector creation
**Location**: `/web/src/app/build/v1/configure/utils/createBuildConnector.ts:52-61`
**Problem**: The `createConnector` call passes `access_type: "private"` but doesn't include `groups: []`. While the backend defaults to empty list, it's better to be explicit.
**Status**: ✅ Will fix
### Issue 2: Step flow logic - Advanced values only connectors show as 2-step
**Location**: `/web/src/app/build/v1/configure/components/ConfigureConnectorModal.tsx:24-36`
**Problem**: The `connectorNeedsConfigStep` function checks both `values` and `advanced_values`. Connectors that only have `advanced_values` (like Slack) incorrectly show as 2-step when they should be 1-step.
**Connectors affected**:
- **Slack**: `values: []`, `advanced_values: [channels, channel_regex_enabled]` → Currently 2-step, should be 1-step
**Status**: ✅ Will fix
## Connector-by-Connector Analysis
### 1. GoogleDrive ✅
- **Config**: Has `values` (indexing_scope tab with fields) and `advanced_values` (specific_user_emails, exclude_domain_link_only)
- **Step Flow**: 2-step ✅ CORRECT
- **access_type**: ✅ Included in createConnector call
- **groups**: ❌ Missing (will add)
### 2. Gmail ✅
- **Config**: `values: []`, `advanced_values: []`
- **Step Flow**: 1-step ✅ CORRECT
- **access_type**: ✅ Included in createConnector call
- **groups**: ❌ Missing (will add)
### 3. Notion ✅
- **Config**: Has `values` (root_page_id), `advanced_values: []`
- **Step Flow**: 2-step ✅ CORRECT
- **access_type**: ✅ Included in createConnector call
- **groups**: ❌ Missing (will add)
### 4. GitHub ✅
- **Config**: Has `values` (repo_owner, github_mode, include_prs, include_issues), `advanced_values: []`
- **Step Flow**: 2-step ✅ CORRECT
- **access_type**: ✅ Included in createConnector call
- **groups**: ❌ Missing (will add)
### 5. Slack ❌
- **Config**: `values: []`, `advanced_values: [channels, channel_regex_enabled]`
- **Step Flow**: 2-step ❌ INCORRECT (should be 1-step)
- **access_type**: ✅ Included in createConnector call
- **groups**: ❌ Missing (will add)
- **Fix**: Update `connectorNeedsConfigStep` to only check `values`, not `advanced_values`
### 6. Linear ✅
- **Config**: `values: []`, `advanced_values: []`
- **Step Flow**: 1-step ✅ CORRECT
- **access_type**: ✅ Included in createConnector call
- **groups**: ❌ Missing (will add)
### 7. Fireflies ✅
- **Config**: `values: []`, `advanced_values: []`
- **Step Flow**: 1-step ✅ CORRECT
- **access_type**: ✅ Included in createConnector call
- **groups**: ❌ Missing (will add)
### 8. Hubspot ✅
- **Config**: Has `values` (object_types), `advanced_values: []`
- **Step Flow**: 2-step ✅ CORRECT
- **access_type**: ✅ Included in createConnector call
- **groups**: ❌ Missing (will add)
## Summary
- **Total connectors**: 8
- **Step flow issues**: 1 (Slack)
- **access_type issues**: 0 (all connectors include it)
- **groups issues**: 8 (all missing, but backend defaults to [])
## Fixes Required
1. Add `groups: []` to `createConnector` call in `createBuildConnector.ts`
2. Update `connectorNeedsConfigStep` to only check `values`, not `advanced_values`

View File

@@ -0,0 +1,150 @@
FROM python:3.11.7-slim-bookworm
LABEL com.danswer.maintainer="founders@onyx.app"
LABEL com.danswer.description="This image is the web/frontend container of Onyx which \
contains code for both the Community and Enterprise editions of Onyx. If you do not \
have a contract or agreement with DanswerAI, you are not permitted to use the Enterprise \
Edition features outside of personal development or testing purposes. Please reach out to \
founders@onyx.app for more information. Please visit https://github.com/onyx-dot-app/onyx"
# DO_NOT_TRACK is used to disable telemetry for Unstructured
ENV DANSWER_RUNNING_IN_DOCKER="true" \
DO_NOT_TRACK="true" \
PLAYWRIGHT_BROWSERS_PATH="/app/.cache/ms-playwright"
# Create non-root user for security best practices
RUN groupadd -g 1001 onyx && \
useradd -u 1001 -g onyx -m -s /bin/bash onyx && \
mkdir -p /var/log/onyx && \
chmod 755 /var/log/onyx && \
chown onyx:onyx /var/log/onyx
COPY --from=ghcr.io/astral-sh/uv:0.9.9 /uv /uvx /bin/
# Install system dependencies
# cmake needed for psycopg (postgres)
# libpq-dev needed for psycopg (postgres)
# curl included just for users' convenience
# zip for Vespa step futher down
# ca-certificates for HTTPS
# nodejs and npm needed for building Next.js template
RUN apt-get update && \
apt-get install -y \
cmake \
curl \
zip \
ca-certificates \
libgnutls30 \
libblkid1 \
libmount1 \
libsmartcols1 \
libuuid1 \
libxmlsec1-dev \
pkg-config \
gcc \
nano \
vim \
nodejs \
npm && \
rm -rf /var/lib/apt/lists/* && \
apt-get clean
# Install Python dependencies
# Remove py which is pulled in by retry, py is not needed and is a CVE
COPY ./requirements/default.txt /tmp/requirements.txt
COPY ./requirements/ee.txt /tmp/ee-requirements.txt
RUN uv pip install --system --no-cache-dir --upgrade \
-r /tmp/requirements.txt \
-r /tmp/ee-requirements.txt && \
pip uninstall -y py && \
playwright install chromium && \
playwright install-deps chromium && \
chown -R onyx:onyx /app && \
ln -s /usr/local/bin/supervisord /usr/bin/supervisord && \
# Cleanup for CVEs and size reduction
# https://github.com/tornadoweb/tornado/issues/3107
# xserver-common and xvfb included by playwright installation but not needed after
# perl-base is part of the base Python Debian image but not needed for Onyx functionality
# perl-base could only be removed with --allow-remove-essential
apt-get update && \
apt-get remove -y --allow-remove-essential \
perl-base \
xserver-common \
xvfb \
cmake \
libldap-2.5-0 \
libxmlsec1-dev \
pkg-config \
gcc && \
# Install here to avoid some packages being cleaned up above
apt-get install -y \
libxmlsec1-openssl \
# Install postgresql-client for easy manual tests
postgresql-client && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/* && \
rm -rf ~/.cache/uv /tmp/*.txt && \
rm -f /usr/local/lib/python3.11/site-packages/tornado/test/test.key
# Build sandbox templates
# Copy web template from the backend package (versioned with code)
COPY --chown=onyx:onyx ./onyx/server/features/build/templates/outputs/web /templates/outputs/web
# Build Python venv template
COPY --chown=onyx:onyx ./onyx/server/features/build/sandbox/kubernetes/docker/initial-requirements.txt /tmp/sandbox-requirements.txt
COPY --chown=onyx:onyx ./onyx/server/features/build/sandbox/util/build_venv_template.py /tmp/build_venv_template.py
RUN mkdir -p /templates && \
python /tmp/build_venv_template.py --requirements /tmp/sandbox-requirements.txt && \
chown -R onyx:onyx /templates && \
rm -f /tmp/build_venv_template.py /tmp/sandbox-requirements.txt
# Pre-downloading models for setups with limited egress
RUN python -c "from tokenizers import Tokenizer; \
Tokenizer.from_pretrained('nomic-ai/nomic-embed-text-v1')"
# Pre-downloading NLTK for setups with limited egress
RUN python -c "import nltk; \
nltk.download('stopwords', quiet=True); \
nltk.download('punkt_tab', quiet=True);"
# nltk.download('wordnet', quiet=True); introduce this back if lemmatization is needed
# Pre-downloading tiktoken for setups with limited egress
RUN python -c "import tiktoken; \
tiktoken.get_encoding('cl100k_base')"
# Set up application files
WORKDIR /app
# Enterprise Version Files
COPY --chown=onyx:onyx ./ee /app/ee
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Set up application files
COPY --chown=onyx:onyx ./onyx /app/onyx
COPY --chown=onyx:onyx ./shared_configs /app/shared_configs
COPY --chown=onyx:onyx ./alembic /app/alembic
COPY --chown=onyx:onyx ./alembic_tenants /app/alembic_tenants
COPY --chown=onyx:onyx ./alembic.ini /app/alembic.ini
COPY supervisord.conf /usr/etc/supervisord.conf
COPY --chown=onyx:onyx ./static /app/static
# Escape hatch scripts
COPY --chown=onyx:onyx ./scripts/debugging /app/scripts/debugging
COPY --chown=onyx:onyx ./scripts/force_delete_connector_by_id.py /app/scripts/force_delete_connector_by_id.py
COPY --chown=onyx:onyx ./scripts/supervisord_entrypoint.sh /app/scripts/supervisord_entrypoint.sh
RUN chmod +x /app/scripts/supervisord_entrypoint.sh
# Put logo in assets
COPY --chown=onyx:onyx ./assets /app/assets
ENV PYTHONPATH=/app
# Default ONYX_VERSION, typically overriden during builds by GitHub Actions.
ARG ONYX_VERSION=0.0.0-dev
ENV ONYX_VERSION=${ONYX_VERSION}
# Default command which does nothing
# This container is used by api server and background which specify their own CMD
CMD ["tail", "-f", "/dev/null"]

View File

@@ -0,0 +1,33 @@
"""add_processing_mode_to_connector_credential_pair
Revision ID: 0ab5805121ef
Revises: 7cd906f37fc6
Create Date: 2026-01-20 15:49:44.136116
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "0ab5805121ef"
down_revision = "7cd906f37fc6"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"connector_credential_pair",
sa.Column(
"processing_mode",
sa.String(),
nullable=False,
server_default="REGULAR",
),
)
def downgrade() -> None:
op.drop_column("connector_credential_pair", "processing_mode")

View File

@@ -0,0 +1,121 @@
"""snapshot_use_sandbox_id_foreign_key
Revision ID: 111d7192d457
Revises: 0ab5805121ef
Create Date: 2026-01-22 16:21:41.711611
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "111d7192d457"
down_revision = "0ab5805121ef"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add sandbox_id column (nullable initially for data migration)
op.add_column(
"snapshot",
sa.Column(
"sandbox_id",
postgresql.UUID(as_uuid=True),
nullable=True,
),
)
# Populate sandbox_id from sandbox table via session_id
op.execute(
"""
UPDATE snapshot
SET sandbox_id = sandbox.id
FROM sandbox
WHERE snapshot.session_id = sandbox.session_id
"""
)
# Make sandbox_id not nullable
op.alter_column("snapshot", "sandbox_id", nullable=False)
# Add foreign key constraint for sandbox_id
op.create_foreign_key(
"snapshot_sandbox_id_fkey",
"snapshot",
"sandbox",
["sandbox_id"],
["id"],
ondelete="CASCADE",
)
# Drop the old index that used session_id
op.drop_index("ix_snapshot_session_created", table_name="snapshot")
# Drop the foreign key constraint on session_id
op.drop_constraint("snapshot_session_id_fkey", "snapshot", type_="foreignkey")
# Drop the session_id column
op.drop_column("snapshot", "session_id")
# Create new index using sandbox_id
op.create_index(
"ix_snapshot_sandbox_created",
"snapshot",
["sandbox_id", sa.text("created_at DESC")],
unique=False,
)
def downgrade() -> None:
# Drop the new index
op.drop_index("ix_snapshot_sandbox_created", table_name="snapshot")
# Add session_id column back (nullable initially for data migration)
op.add_column(
"snapshot",
sa.Column(
"session_id",
postgresql.UUID(as_uuid=True),
nullable=True,
),
)
# Populate session_id from sandbox table
op.execute(
"""
UPDATE snapshot
SET session_id = sandbox.session_id
FROM sandbox
WHERE snapshot.sandbox_id = sandbox.id
"""
)
# Make session_id not nullable
op.alter_column("snapshot", "session_id", nullable=False)
# Add foreign key constraint for session_id
op.create_foreign_key(
"snapshot_session_id_fkey",
"snapshot",
"build_session",
["session_id"],
["id"],
ondelete="CASCADE",
)
# Recreate the old index
op.create_index(
"ix_snapshot_session_created",
"snapshot",
["session_id", sa.text("created_at DESC")],
unique=False,
)
# Drop the foreign key constraint on sandbox_id
op.drop_constraint("snapshot_sandbox_id_fkey", "snapshot", type_="foreignkey")
# Drop the sandbox_id column
op.drop_column("snapshot", "sandbox_id")

View File

@@ -0,0 +1,89 @@
"""create_build_message_table
Revision ID: 26b589bf8be7
Revises: df6cbd9a37cc
Create Date: 2026-01-19 17:51:08.289325
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "26b589bf8be7"
down_revision = "df6cbd9a37cc"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Reuse existing messagetype enum from chat_message table
# Build messages only use: USER, ASSISTANT, SYSTEM
# Note: The existing enum has uppercase values but MessageType in code uses lowercase
# This works because SQLAlchemy handles the conversion when native_enum=False
# Create build_message table
# Schema notes:
# - turn_index: 0-indexed user message number; groups all assistant responses
# (tool calls, thoughts, messages, plans) under the user prompt they respond to
# - message_metadata: Required JSONB field containing the raw ACP packet JSON
# - No content column: all data is stored in message_metadata
op.create_table(
"build_message",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
sa.Column(
"session_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("build_session.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column(
"turn_index",
sa.Integer(),
nullable=False,
),
sa.Column(
"type",
sa.Enum(
"SYSTEM",
"USER",
"ASSISTANT",
"DANSWER",
name="messagetype",
create_type=False,
native_enum=False,
),
nullable=False,
),
sa.Column(
"message_metadata",
postgresql.JSONB(),
nullable=False,
),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.PrimaryKeyConstraint("id"),
)
# Create composite index for efficient turn-based queries
# Orders by session_id, turn_index, then created_at for proper message ordering
op.create_index(
"ix_build_message_session_turn",
"build_message",
["session_id", "turn_index", sa.text("created_at ASC")],
unique=False,
)
def downgrade() -> None:
# Drop index
op.drop_index("ix_build_message_session_turn", table_name="build_message")
# Drop table
op.drop_table("build_message")

View File

@@ -0,0 +1,84 @@
"""create_sandbox_table
Revision ID: 484b9fa1ac89
Revises: 96086064c5db
Create Date: 2026-01-19 14:47:52.829749
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "484b9fa1ac89"
down_revision = "96086064c5db"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Create sandbox status enum
sandbox_status_enum = sa.Enum(
"provisioning",
"running",
"idle",
"terminated",
name="sandboxstatus",
native_enum=False,
)
# Create sandbox table
op.create_table(
"sandbox",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
sa.Column(
"session_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("build_session.id", ondelete="CASCADE"),
nullable=False,
unique=True,
),
sa.Column("container_id", sa.String(), nullable=True),
sa.Column(
"status",
sandbox_status_enum,
nullable=False,
server_default="provisioning",
),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column("last_heartbeat", sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
# Create indexes for sandbox
op.create_index(
"ix_sandbox_status",
"sandbox",
["status"],
unique=False,
)
op.create_index(
"ix_sandbox_container_id",
"sandbox",
["container_id"],
unique=False,
)
def downgrade() -> None:
# Drop indexes
op.drop_index("ix_sandbox_container_id", table_name="sandbox")
op.drop_index("ix_sandbox_status", table_name="sandbox")
# Drop table
op.drop_table("sandbox")
# Drop enum
sa.Enum(name="sandboxstatus").drop(op.get_bind(), checkfirst=True)

View File

@@ -0,0 +1,268 @@
"""User shared sandbox - Phase 1 schema changes
Changes Sandbox from session-owned to user-owned (one sandbox per user),
and changes Snapshot from sandbox-linked to session-linked.
Sandbox table:
- Remove: session_id (unique FK to BuildSession)
- Add: user_id (FK to User, NOT NULL, unique)
Snapshot table:
- Remove: sandbox_id (FK to Sandbox)
- Add: session_id (FK to BuildSession, NOT NULL, ondelete=CASCADE)
- Update index: ix_snapshot_sandbox_created -> ix_snapshot_session_created
Revision ID: 6db00b8237e5
Revises: 111d7192d457
Create Date: 2026-01-23 12:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "6db00b8237e5"
down_revision = "111d7192d457"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ==========================================================================
# SNAPSHOT: Change from sandbox_id to session_id
# Must be done BEFORE dropping sandbox.session_id (needed for data migration)
# ==========================================================================
# 1. Add session_id column (nullable initially for data migration)
op.add_column(
"snapshot",
sa.Column(
"session_id",
postgresql.UUID(as_uuid=True),
nullable=True,
),
)
# 2. Populate session_id from sandbox.session_id via snapshot.sandbox_id
op.execute(
"""
UPDATE snapshot
SET session_id = sandbox.session_id
FROM sandbox
WHERE snapshot.sandbox_id = sandbox.id
"""
)
# 3. Make session_id not nullable
op.alter_column("snapshot", "session_id", nullable=False)
# 4. Add FK constraint for session_id
op.create_foreign_key(
"snapshot_session_id_fkey",
"snapshot",
"build_session",
["session_id"],
["id"],
ondelete="CASCADE",
)
# 5. Drop old index
op.drop_index("ix_snapshot_sandbox_created", table_name="snapshot")
# 6. Drop FK constraint on sandbox_id
op.drop_constraint("snapshot_sandbox_id_fkey", "snapshot", type_="foreignkey")
# 7. Drop sandbox_id column
op.drop_column("snapshot", "sandbox_id")
# 8. Create new index
op.create_index(
"ix_snapshot_session_created",
"snapshot",
["session_id", sa.text("created_at DESC")],
unique=False,
)
# ==========================================================================
# SANDBOX: Change from session_id to user_id
# ==========================================================================
# 1. Add user_id column (nullable initially for data migration)
op.add_column(
"sandbox",
sa.Column(
"user_id",
postgresql.UUID(as_uuid=True),
nullable=True,
),
)
# 2. Populate user_id from build_session.user_id via sandbox.session_id
op.execute(
"""
UPDATE sandbox
SET user_id = build_session.user_id
FROM build_session
WHERE sandbox.session_id = build_session.id
"""
)
# 3. Delete any sandboxes that couldn't be mapped (orphaned data)
# This handles sandboxes whose sessions have NULL user_id or were deleted
op.execute(
"""
DELETE FROM sandbox WHERE user_id IS NULL
"""
)
# 4. Make user_id not nullable
op.alter_column("sandbox", "user_id", nullable=False)
# 5. Drop the unique constraint on session_id
op.drop_constraint("sandbox_session_id_key", "sandbox", type_="unique")
# 6. Drop FK constraint on session_id
op.drop_constraint("sandbox_session_id_fkey", "sandbox", type_="foreignkey")
# 7. Drop session_id column
op.drop_column("sandbox", "session_id")
# 8. Add FK constraint for user_id
op.create_foreign_key(
"sandbox_user_id_fkey",
"sandbox",
"user",
["user_id"],
["id"],
ondelete="CASCADE",
)
# 9. Add unique constraint on user_id (one sandbox per user)
op.create_unique_constraint("sandbox_user_id_key", "sandbox", ["user_id"])
def downgrade() -> None:
# ==========================================================================
# SANDBOX: Change back from user_id to session_id
# ==========================================================================
# 1. Drop unique constraint on user_id
op.drop_constraint("sandbox_user_id_key", "sandbox", type_="unique")
# 2. Drop FK constraint on user_id
op.drop_constraint("sandbox_user_id_fkey", "sandbox", type_="foreignkey")
# 3. Add session_id column back (nullable initially)
op.add_column(
"sandbox",
sa.Column(
"session_id",
postgresql.UUID(as_uuid=True),
nullable=True,
),
)
# 4. NOTE: Cannot reliably restore session_id data since the relationship
# is now one-to-many (user can have multiple sessions).
# Set session_id to a random session of the user for data integrity.
op.execute(
"""
UPDATE sandbox
SET session_id = (
SELECT build_session.id
FROM build_session
WHERE build_session.user_id = sandbox.user_id
ORDER BY build_session.created_at DESC
LIMIT 1
)
"""
)
# 5. Delete sandboxes that couldn't be mapped
op.execute(
"""
DELETE FROM sandbox WHERE session_id IS NULL
"""
)
# 6. Make session_id not nullable
op.alter_column("sandbox", "session_id", nullable=False)
# 7. Add FK constraint for session_id
op.create_foreign_key(
"sandbox_session_id_fkey",
"sandbox",
"build_session",
["session_id"],
["id"],
ondelete="CASCADE",
)
# 8. Add unique constraint on session_id
op.create_unique_constraint("sandbox_session_id_key", "sandbox", ["session_id"])
# 9. Drop user_id column
op.drop_column("sandbox", "user_id")
# ==========================================================================
# SNAPSHOT: Change back from session_id to sandbox_id
# ==========================================================================
# 1. Drop new index
op.drop_index("ix_snapshot_session_created", table_name="snapshot")
# 2. Add sandbox_id column back (nullable initially)
op.add_column(
"snapshot",
sa.Column(
"sandbox_id",
postgresql.UUID(as_uuid=True),
nullable=True,
),
)
# 3. Populate sandbox_id from the newly restored sandbox.session_id
op.execute(
"""
UPDATE snapshot
SET sandbox_id = sandbox.id
FROM sandbox
WHERE snapshot.session_id = sandbox.session_id
"""
)
# 4. Delete snapshots that couldn't be mapped
op.execute(
"""
DELETE FROM snapshot WHERE sandbox_id IS NULL
"""
)
# 5. Make sandbox_id not nullable
op.alter_column("snapshot", "sandbox_id", nullable=False)
# 6. Add FK constraint for sandbox_id
op.create_foreign_key(
"snapshot_sandbox_id_fkey",
"snapshot",
"sandbox",
["sandbox_id"],
["id"],
ondelete="CASCADE",
)
# 7. Create old index
op.create_index(
"ix_snapshot_sandbox_created",
"snapshot",
["sandbox_id", sa.text("created_at DESC")],
unique=False,
)
# 8. Drop FK constraint on session_id
op.drop_constraint("snapshot_session_id_fkey", "snapshot", type_="foreignkey")
# 9. Drop session_id column
op.drop_column("snapshot", "session_id")

View File

@@ -0,0 +1,37 @@
"""move nextjs_port from sandbox to build_session
Revision ID: 76dd1eb17d31
Revises: 6db00b8237e5
Create Date: 2026-01-23 16:24:36.965851
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "76dd1eb17d31"
down_revision = "6db00b8237e5"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Add nextjs_port column to build_session (per-session port allocation)
op.add_column(
"build_session",
sa.Column("nextjs_port", sa.Integer(), nullable=True),
)
# Drop nextjs_port column from sandbox (no longer needed at sandbox level)
op.drop_column("sandbox", "nextjs_port")
def downgrade() -> None:
# Add nextjs_port back to sandbox
op.add_column(
"sandbox",
sa.Column("nextjs_port", sa.Integer(), nullable=True),
)
# Drop nextjs_port from build_session
op.drop_column("build_session", "nextjs_port")

View File

@@ -0,0 +1,27 @@
"""add nextjs_port to sandbox
Revision ID: 7cd906f37fc6
Revises: 26b589bf8be7
Create Date: 2026-01-20
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "7cd906f37fc6"
down_revision: Union[str, None] = "26b589bf8be7"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column("sandbox", sa.Column("nextjs_port", sa.Integer(), nullable=True))
def downgrade() -> None:
op.drop_column("sandbox", "nextjs_port")

View File

@@ -0,0 +1,86 @@
"""create_build_session_table
Revision ID: 96086064c5db
Revises: 41fa44bef321
Create Date: 2026-01-19 14:47:38.156803
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "96086064c5db"
down_revision = "41fa44bef321"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Create build_session status enum
build_session_status_enum = sa.Enum(
"active",
"idle",
name="buildsessionstatus",
native_enum=False,
)
# Create build_session table
op.create_table(
"build_session",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
sa.Column(
"user_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("user.id", ondelete="CASCADE"),
nullable=True,
),
sa.Column("name", sa.String(), nullable=True),
sa.Column(
"status",
build_session_status_enum,
nullable=False,
server_default="active",
),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column(
"last_activity_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.PrimaryKeyConstraint("id"),
)
# Create indexes for build_session
op.create_index(
"ix_build_session_user_created",
"build_session",
["user_id", sa.text("created_at DESC")],
unique=False,
)
op.create_index(
"ix_build_session_status",
"build_session",
["status"],
unique=False,
)
def downgrade() -> None:
# Drop indexes
op.drop_index("ix_build_session_status", table_name="build_session")
op.drop_index("ix_build_session_user_created", table_name="build_session")
# Drop table
op.drop_table("build_session")
# Drop enum
sa.Enum(name="buildsessionstatus").drop(op.get_bind(), checkfirst=True)

View File

@@ -0,0 +1,86 @@
"""create_artifact_table
Revision ID: a441232d9c5a
Revises: 484b9fa1ac89
Create Date: 2026-01-19 14:47:57.226496
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "a441232d9c5a"
down_revision = "484b9fa1ac89"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Create artifact type enum
artifact_type_enum = sa.Enum(
"web_app",
"pptx",
"docx",
"markdown",
"excel",
"image",
name="artifacttype",
native_enum=False,
)
# Create artifact table
op.create_table(
"artifact",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
sa.Column(
"session_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("build_session.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("type", artifact_type_enum, nullable=False),
sa.Column("path", sa.String(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.PrimaryKeyConstraint("id"),
)
# Create indexes for artifact
op.create_index(
"ix_artifact_session_created",
"artifact",
["session_id", sa.text("created_at DESC")],
unique=False,
)
op.create_index(
"ix_artifact_type",
"artifact",
["type"],
unique=False,
)
def downgrade() -> None:
# Drop indexes
op.drop_index("ix_artifact_type", table_name="artifact")
op.drop_index("ix_artifact_session_created", table_name="artifact")
# Drop table
op.drop_table("artifact")
# Drop enum
sa.Enum(name="artifacttype").drop(op.get_bind(), checkfirst=True)

View File

@@ -0,0 +1,57 @@
"""create_snapshot_table
Revision ID: df6cbd9a37cc
Revises: a441232d9c5a
Create Date: 2026-01-19 14:48:00.757530
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "df6cbd9a37cc"
down_revision = "a441232d9c5a"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Create snapshot table (no enum needed)
op.create_table(
"snapshot",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
sa.Column(
"session_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("build_session.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("storage_path", sa.String(), nullable=False),
sa.Column("size_bytes", sa.BigInteger(), nullable=False, server_default="0"),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.PrimaryKeyConstraint("id"),
)
# Create index for snapshot
op.create_index(
"ix_snapshot_session_created",
"snapshot",
["session_id", sa.text("created_at DESC")],
unique=False,
)
def downgrade() -> None:
# Drop index
op.drop_index("ix_snapshot_session_created", table_name="snapshot")
# Drop table
op.drop_table("snapshot")

View File

@@ -134,5 +134,7 @@ celery_app.autodiscover_tasks(
"onyx.background.celery.tasks.docprocessing",
# Docfetching worker tasks
"onyx.background.celery.tasks.docfetching",
# Sandbox cleanup tasks (isolated in build feature)
"onyx.server.features.build.sandbox.tasks",
]
)

View File

@@ -116,5 +116,7 @@ celery_app.autodiscover_tasks(
"onyx.background.celery.tasks.connector_deletion",
"onyx.background.celery.tasks.doc_permission_syncing",
"onyx.background.celery.tasks.docprocessing",
# Sandbox cleanup tasks (isolated in build feature)
"onyx.server.features.build.sandbox.tasks",
]
)

View File

@@ -139,6 +139,27 @@ beat_task_templates: list[dict] = [
"queue": OnyxCeleryQueues.MONITORING,
},
},
# Sandbox cleanup tasks
{
"name": "cleanup-idle-sandboxes",
"task": OnyxCeleryTask.CLEANUP_IDLE_SANDBOXES,
"schedule": timedelta(minutes=1),
"options": {
"priority": OnyxCeleryPriority.LOW,
"expires": BEAT_EXPIRES_DEFAULT,
"queue": OnyxCeleryQueues.SANDBOX,
},
},
{
"name": "cleanup-old-snapshots",
"task": OnyxCeleryTask.CLEANUP_OLD_SNAPSHOTS,
"schedule": timedelta(hours=24),
"options": {
"priority": OnyxCeleryPriority.LOW,
"expires": BEAT_EXPIRES_DEFAULT,
"queue": OnyxCeleryQueues.SANDBOX,
},
},
]
if ENTERPRISE_EDITION_ENABLED:

View File

@@ -31,17 +31,20 @@ from onyx.connectors.interfaces import CheckpointedConnector
from onyx.connectors.models import ConnectorFailure
from onyx.connectors.models import ConnectorStopSignal
from onyx.connectors.models import Document
from onyx.connectors.models import IndexAttemptMetadata
from onyx.connectors.models import TextSection
from onyx.db.connector import mark_ccpair_with_indexing_trigger
from onyx.db.connector_credential_pair import get_connector_credential_pair_from_id
from onyx.db.connector_credential_pair import get_last_successful_attempt_poll_range_end
from onyx.db.connector_credential_pair import update_connector_credential_pair
from onyx.db.constants import CONNECTOR_VALIDATION_ERROR_MESSAGE_PREFIX
from onyx.db.document import mark_document_as_indexed_for_cc_pair__no_commit
from onyx.db.engine.sql_engine import get_session_with_current_tenant
from onyx.db.enums import AccessType
from onyx.db.enums import ConnectorCredentialPairStatus
from onyx.db.enums import IndexingStatus
from onyx.db.enums import IndexModelStatus
from onyx.db.enums import ProcessingMode
from onyx.db.index_attempt import create_index_attempt_error
from onyx.db.index_attempt import get_index_attempt
from onyx.db.index_attempt import get_recent_completed_attempts_for_cc_pair
@@ -53,7 +56,10 @@ from onyx.db.models import IndexAttempt
from onyx.file_store.document_batch_storage import DocumentBatchStorage
from onyx.file_store.document_batch_storage import get_document_batch_storage
from onyx.indexing.indexing_heartbeat import IndexingHeartbeatInterface
from onyx.indexing.indexing_pipeline import index_doc_batch_prepare
from onyx.indexing.persistent_document_writer import get_persistent_document_writer
from onyx.utils.logger import setup_logger
from onyx.utils.middleware import make_randomized_onyx_request_id
from onyx.utils.variable_functionality import global_version
from shared_configs.configs import MULTI_TENANT
from shared_configs.contextvars import INDEX_ATTEMPT_INFO_CONTEXTVAR
@@ -367,6 +373,7 @@ def connector_document_extraction(
db_connector = index_attempt.connector_credential_pair.connector
db_credential = index_attempt.connector_credential_pair.credential
processing_mode = index_attempt.connector_credential_pair.processing_mode
is_primary = index_attempt.search_settings.status == IndexModelStatus.PRESENT
from_beginning = index_attempt.from_beginning
@@ -600,34 +607,100 @@ def connector_document_extraction(
logger.debug(f"Indexing batch of documents: {batch_description}")
memory_tracer.increment_and_maybe_trace()
# Store documents in storage
batch_storage.store_batch(batch_num, doc_batch_cleaned)
# cc4a
if processing_mode == ProcessingMode.FILE_SYSTEM:
# File system only - write directly to persistent storage,
# skip chunking/embedding/Vespa but still track documents in DB
# Create processing task data
processing_batch_data = {
"index_attempt_id": index_attempt_id,
"cc_pair_id": cc_pair_id,
"tenant_id": tenant_id,
"batch_num": batch_num, # 0-indexed
}
with get_session_with_current_tenant() as db_session:
# Create metadata for the batch
index_attempt_metadata = IndexAttemptMetadata(
attempt_id=index_attempt_id,
connector_id=db_connector.id,
credential_id=db_credential.id,
request_id=make_randomized_onyx_request_id("FSI"),
structured_id=f"{tenant_id}:{cc_pair_id}:{index_attempt_id}:{batch_num}",
batch_num=batch_num,
)
# Queue document processing task
app.send_task(
OnyxCeleryTask.DOCPROCESSING_TASK,
kwargs=processing_batch_data,
queue=OnyxCeleryQueues.DOCPROCESSING,
priority=docprocessing_priority,
)
# Upsert documents to PostgreSQL (document table + cc_pair relationship)
# This is a subset of what docprocessing does - just DB tracking, no chunking/embedding
index_doc_batch_prepare(
documents=doc_batch_cleaned,
index_attempt_metadata=index_attempt_metadata,
db_session=db_session,
ignore_time_skip=True, # Documents already filtered during extraction
)
batch_num += 1
total_doc_batches_queued += 1
# Mark documents as indexed for the CC pair
mark_document_as_indexed_for_cc_pair__no_commit(
connector_id=db_connector.id,
credential_id=db_credential.id,
document_ids=[doc.id for doc in doc_batch_cleaned],
db_session=db_session,
)
db_session.commit()
logger.info(
f"Queued document processing batch: "
f"batch_num={batch_num} "
f"docs={len(doc_batch_cleaned)} "
f"attempt={index_attempt_id}"
)
# Write documents to persistent file system
# Use creator_id for user-segregated storage paths (sandbox isolation)
creator_id = index_attempt.connector_credential_pair.creator_id
if creator_id is None:
raise ValueError(
f"ConnectorCredentialPair {index_attempt.connector_credential_pair.id} "
"must have a creator_id for persistent document storage"
)
user_id_str: str = str(creator_id)
writer = get_persistent_document_writer(user_id=user_id_str)
written_paths = writer.write_documents(doc_batch_cleaned)
# Update coordination directly (no docprocessing task)
with get_session_with_current_tenant() as db_session:
IndexingCoordination.update_batch_completion_and_docs(
db_session=db_session,
index_attempt_id=index_attempt_id,
total_docs_indexed=len(doc_batch_cleaned),
new_docs_indexed=len(doc_batch_cleaned),
total_chunks=0, # No chunks for file system mode
)
batch_num += 1
total_doc_batches_queued += 1
logger.info(
f"Wrote documents to file system: "
f"batch_num={batch_num} "
f"docs={len(written_paths)} "
f"attempt={index_attempt_id}"
)
else:
# REGULAR mode (default): Full pipeline - store and queue docprocessing
batch_storage.store_batch(batch_num, doc_batch_cleaned)
# Create processing task data
processing_batch_data = {
"index_attempt_id": index_attempt_id,
"cc_pair_id": cc_pair_id,
"tenant_id": tenant_id,
"batch_num": batch_num, # 0-indexed
}
# Queue document processing task
app.send_task(
OnyxCeleryTask.DOCPROCESSING_TASK,
kwargs=processing_batch_data,
queue=OnyxCeleryQueues.DOCPROCESSING,
priority=docprocessing_priority,
)
batch_num += 1
total_doc_batches_queued += 1
logger.info(
f"Queued document processing batch: "
f"batch_num={batch_num} "
f"docs={len(doc_batch_cleaned)} "
f"attempt={index_attempt_id}"
)
# Check checkpoint size periodically
CHECKPOINT_SIZE_CHECK_INTERVAL = 100

View File

@@ -1042,3 +1042,14 @@ STRIPE_PUBLISHABLE_KEY_URL = (
)
# Override for local testing with Stripe test keys (pk_test_*)
STRIPE_PUBLISHABLE_KEY_OVERRIDE = os.environ.get("STRIPE_PUBLISHABLE_KEY")
# Persistent Document Storage Configuration
# When enabled, indexed documents are written to local filesystem with hierarchical structure
PERSISTENT_DOCUMENT_STORAGE_ENABLED = (
os.environ.get("PERSISTENT_DOCUMENT_STORAGE_ENABLED", "").lower() == "true"
)
# Base directory path for persistent document storage (local filesystem)
# Example: /var/onyx/indexed-docs or /app/indexed-docs
PERSISTENT_DOCUMENT_STORAGE_PATH = os.environ.get(
"PERSISTENT_DOCUMENT_STORAGE_PATH", "/app/indexed-docs"
)

View File

@@ -241,6 +241,7 @@ class NotificationType(str, Enum):
TRIAL_ENDS_TWO_DAYS = "two_day_trial_ending" # 2 days left in trial
RELEASE_NOTES = "release_notes"
ASSISTANT_FILES_READY = "assistant_files_ready"
FEATURE_ANNOUNCEMENT = "feature_announcement"
class BlobType(str, Enum):
@@ -327,6 +328,7 @@ class FileOrigin(str, Enum):
PLAINTEXT_CACHE = "plaintext_cache"
OTHER = "other"
QUERY_HISTORY_CSV = "query_history_csv"
SANDBOX_SNAPSHOT = "sandbox_snapshot"
USER_FILE = "user_file"
@@ -344,6 +346,7 @@ class MilestoneRecordType(str, Enum):
MULTIPLE_ASSISTANTS = "multiple_assistants"
CREATED_ASSISTANT = "created_assistant"
CREATED_ONYX_BOT = "created_onyx_bot"
REQUESTED_CONNECTOR = "requested_connector"
class PostgresAdvisoryLocks(Enum):
@@ -383,6 +386,9 @@ class OnyxCeleryQueues:
# KG processing queue
KG_PROCESSING = "kg_processing"
# Sandbox processing queue
SANDBOX = "sandbox"
class OnyxRedisLocks:
PRIMARY_WORKER = "da_lock:primary_worker"
@@ -431,6 +437,10 @@ class OnyxRedisLocks:
# Release notes
RELEASE_NOTES_FETCH_LOCK = "da_lock:release_notes_fetch"
# Sandbox cleanup
CLEANUP_IDLE_SANDBOXES_BEAT_LOCK = "da_lock:cleanup_idle_sandboxes_beat"
CLEANUP_OLD_SNAPSHOTS_BEAT_LOCK = "da_lock:cleanup_old_snapshots_beat"
class OnyxRedisSignals:
BLOCK_VALIDATE_INDEXING_FENCES = "signal:block_validate_indexing_fences"
@@ -556,6 +566,10 @@ class OnyxCeleryTask:
CHECK_KG_PROCESSING_CLUSTERING_ONLY = "check_kg_processing_clustering_only"
KG_RESET_SOURCE_INDEX = "kg_reset_source_index"
# Sandbox cleanup
CLEANUP_IDLE_SANDBOXES = "cleanup_idle_sandboxes"
CLEANUP_OLD_SNAPSHOTS = "cleanup_old_snapshots"
# this needs to correspond to the matching entry in supervisord
ONYX_CELERY_BEAT_HEARTBEAT_KEY = "onyx:celery:beat:heartbeat"

View File

@@ -89,6 +89,9 @@ def _create_doc_from_transcript(transcript: dict) -> Document | None:
meeting_date_unix = transcript["date"]
meeting_date = datetime.fromtimestamp(meeting_date_unix / 1000, tz=timezone.utc)
# Build hierarchy based on meeting date (year-month)
year_month = meeting_date.strftime("%Y-%m")
meeting_organizer_email = transcript["organizer_email"]
organizer_email_user_info = [BasicExpertInfo(email=meeting_organizer_email)]
@@ -102,6 +105,14 @@ def _create_doc_from_transcript(transcript: dict) -> Document | None:
sections=cast(list[TextSection | ImageSection], sections),
source=DocumentSource.FIREFLIES,
semantic_identifier=meeting_title,
doc_metadata={
"hierarchy": {
"source_path": [year_month],
"year_month": year_month,
"meeting_title": meeting_title,
"organizer_email": meeting_organizer_email,
}
},
metadata={
k: str(v)
for k, v in {

View File

@@ -240,8 +240,21 @@ def _get_userinfo(user: NamedUser) -> dict[str, str]:
def _convert_pr_to_document(
pull_request: PullRequest, repo_external_access: ExternalAccess | None
) -> Document:
repo_name = pull_request.base.repo.full_name if pull_request.base else ""
doc_metadata = DocMetadata(repo=repo_name)
repo_full_name = pull_request.base.repo.full_name if pull_request.base else ""
# Split full_name (e.g., "owner/repo") into owner and repo
parts = repo_full_name.split("/", 1)
owner_name = parts[0] if parts else ""
repo_name = parts[1] if len(parts) > 1 else repo_full_name
doc_metadata = {
"repo": repo_full_name,
"hierarchy": {
"source_path": [owner_name, repo_name, "pull_requests"],
"owner": owner_name,
"repo": repo_name,
"object_type": "pull_request",
},
}
return Document(
id=pull_request.html_url,
sections=[
@@ -259,7 +272,7 @@ def _convert_pr_to_document(
else None
),
# this metadata is used in perm sync
doc_metadata=doc_metadata.model_dump(),
doc_metadata=doc_metadata,
metadata={
k: [str(vi) for vi in v] if isinstance(v, list) else str(v)
for k, v in {
@@ -316,8 +329,21 @@ def _fetch_issue_comments(issue: Issue) -> str:
def _convert_issue_to_document(
issue: Issue, repo_external_access: ExternalAccess | None
) -> Document:
repo_name = issue.repository.full_name if issue.repository else ""
doc_metadata = DocMetadata(repo=repo_name)
repo_full_name = issue.repository.full_name if issue.repository else ""
# Split full_name (e.g., "owner/repo") into owner and repo
parts = repo_full_name.split("/", 1)
owner_name = parts[0] if parts else ""
repo_name = parts[1] if len(parts) > 1 else repo_full_name
doc_metadata = {
"repo": repo_full_name,
"hierarchy": {
"source_path": [owner_name, repo_name, "issues"],
"owner": owner_name,
"repo": repo_name,
"object_type": "issue",
},
}
return Document(
id=issue.html_url,
sections=[TextSection(link=issue.html_url, text=issue.body or "")],
@@ -327,7 +353,7 @@ def _convert_issue_to_document(
# updated_at is UTC time but is timezone unaware
doc_updated_at=issue.updated_at.replace(tzinfo=timezone.utc),
# this metadata is used in perm sync
doc_metadata=doc_metadata.model_dump(),
doc_metadata=doc_metadata,
metadata={
k: [str(vi) for vi in v] if isinstance(v, list) else str(v)
for k, v in {

View File

@@ -390,7 +390,9 @@ class GmailConnector(
"""
List all user emails if we are on a Google Workspace domain.
If the domain is gmail.com, or if we attempt to call the Admin SDK and
get a 404, fall back to using the single user.
get a 404 or 403, fall back to using the single user.
A 404 indicates a personal Gmail account with no Workspace domain.
A 403 indicates insufficient permissions (e.g., OAuth user without admin privileges).
"""
try:
@@ -413,6 +415,13 @@ class GmailConnector(
"with no Workspace domain. Falling back to single user."
)
return [self.primary_admin_email]
elif e.resp.status == 403:
logger.warning(
"Received 403 from Admin SDK; this may indicate insufficient permissions "
"(e.g., OAuth user without admin privileges or service account without "
"domain-wide delegation). Falling back to single user."
)
return [self.primary_admin_email]
raise
def _fetch_threads_impl(

View File

@@ -46,6 +46,138 @@ from onyx.utils.variable_functionality import noop_fallback
logger = setup_logger()
# Cache for folder path lookups to avoid redundant API calls
# Maps folder_id -> (folder_name, parent_id)
_folder_cache: dict[str, tuple[str, str | None]] = {}
def _get_folder_info(
service: GoogleDriveService, folder_id: str
) -> tuple[str, str | None]:
"""Fetch folder name and parent ID, with caching."""
if folder_id in _folder_cache:
return _folder_cache[folder_id]
try:
folder = (
service.files()
.get(
fileId=folder_id,
fields="name, parents",
supportsAllDrives=True,
)
.execute()
)
folder_name = folder.get("name", "Unknown")
parents = folder.get("parents", [])
parent_id = parents[0] if parents else None
_folder_cache[folder_id] = (folder_name, parent_id)
return folder_name, parent_id
except HttpError as e:
logger.warning(f"Failed to get folder info for {folder_id}: {e}")
_folder_cache[folder_id] = ("Unknown", None)
return "Unknown", None
def _get_drive_name(service: GoogleDriveService, drive_id: str) -> str:
"""Fetch shared drive name."""
cache_key = f"drive_{drive_id}"
if cache_key in _folder_cache:
return _folder_cache[cache_key][0]
try:
drive = service.drives().get(driveId=drive_id).execute()
drive_name = drive.get("name", f"Shared Drive {drive_id}")
_folder_cache[cache_key] = (drive_name, None)
return drive_name
except HttpError as e:
logger.warning(f"Failed to get drive name for {drive_id}: {e}")
_folder_cache[cache_key] = (f"Shared Drive {drive_id}", None)
return f"Shared Drive {drive_id}"
def build_folder_path(
file: GoogleDriveFileType,
service: GoogleDriveService,
drive_id: str | None = None,
user_email: str | None = None,
) -> list[str]:
"""
Build the full folder path for a file by walking up the parent chain.
Returns a list of folder names from root to immediate parent.
Args:
file: The Google Drive file object
service: Google Drive service instance
drive_id: Optional drive ID (will be extracted from file if not provided)
user_email: Optional user email to check ownership for "My Drive" vs "Shared with me"
"""
path_parts: list[str] = []
# Get drive_id from file if not provided
if drive_id is None:
drive_id = file.get("driveId")
# Check if file is owned by the user (for distinguishing "My Drive" vs "Shared with me")
is_owned_by_user = False
if user_email:
owners = file.get("owners", [])
is_owned_by_user = any(
owner.get("emailAddress", "").lower() == user_email.lower()
for owner in owners
)
# Get the file's parent folder ID
parents = file.get("parents", [])
if not parents:
# File is at root level
if drive_id:
return [_get_drive_name(service, drive_id)]
# If not in a shared drive, check if it's owned by the user
if is_owned_by_user:
return ["My Drive"]
else:
return ["Shared with me"]
parent_id: str | None = parents[0]
# Walk up the folder hierarchy (limit to 50 levels to prevent infinite loops)
visited: set[str] = set()
for _ in range(50):
if not parent_id or parent_id in visited:
break
visited.add(parent_id)
folder_name, next_parent = _get_folder_info(service, parent_id)
# Check if we've reached the root (parent is the drive itself or no parent)
if next_parent is None:
# This folder's name is either the drive root, My Drive, or Shared with me
if drive_id:
path_parts.insert(0, _get_drive_name(service, drive_id))
else:
# Not in a shared drive - determine if it's "My Drive" or "Shared with me"
if is_owned_by_user:
path_parts.insert(0, "My Drive")
else:
path_parts.insert(0, "Shared with me")
break
else:
path_parts.insert(0, folder_name)
parent_id = next_parent
# If we didn't find a root, determine the root based on ownership and drive
if not path_parts:
if drive_id:
return [_get_drive_name(service, drive_id)]
elif is_owned_by_user:
return ["My Drive"]
else:
return ["Shared with me"]
return path_parts
# This is not a standard valid unicode char, it is used by the docs advanced API to
# represent smart chips (elements like dates and doc links).
SMART_CHIP_CHAR = "\ue907"
@@ -526,12 +658,33 @@ def _convert_drive_item_to_document(
else None
)
# Build doc_metadata with hierarchy information
file_name = file.get("name", "")
mime_type = file.get("mimeType", "")
drive_id = file.get("driveId")
# Build full folder path by walking up the parent chain
# Pass retriever_email to determine if file is in "My Drive" vs "Shared with me"
source_path = build_folder_path(
file, _get_drive_service(), drive_id, retriever_email
)
doc_metadata = {
"hierarchy": {
"source_path": source_path,
"drive_id": drive_id,
"file_name": file_name,
"mime_type": mime_type,
}
}
# Create the document
return Document(
id=doc_id,
sections=sections,
source=DocumentSource.GOOGLE_DRIVE,
semantic_identifier=file.get("name", ""),
semantic_identifier=file_name,
doc_metadata=doc_metadata,
metadata={
"owner_names": ", ".join(
owner.get("displayName", "") for owner in file.get("owners", [])

View File

@@ -39,11 +39,11 @@ PERMISSION_FULL_DESCRIPTION = (
"permissions(id, emailAddress, type, domain, allowFileDiscovery, permissionDetails)"
)
FILE_FIELDS = (
"nextPageToken, files(mimeType, id, name, "
"nextPageToken, files(mimeType, id, name, driveId, parents, "
"modifiedTime, webViewLink, shortcutDetails, owners(emailAddress), size)"
)
FILE_FIELDS_WITH_PERMISSIONS = (
f"nextPageToken, files(mimeType, id, name, {PERMISSION_FULL_DESCRIPTION}, permissionIds, "
f"nextPageToken, files(mimeType, id, name, driveId, parents, {PERMISSION_FULL_DESCRIPTION}, permissionIds, "
"modifiedTime, webViewLink, shortcutDetails, owners(emailAddress), size)"
)
SLIM_FILE_FIELDS = (

View File

@@ -490,6 +490,13 @@ class HubSpotConnector(LoadConnector, PollConnector):
semantic_identifier=title,
doc_updated_at=ticket.updated_at.replace(tzinfo=timezone.utc),
metadata=metadata,
doc_metadata={
"hierarchy": {
"source_path": ["Tickets"],
"object_type": "ticket",
"object_id": ticket.id,
}
},
)
)
@@ -615,6 +622,13 @@ class HubSpotConnector(LoadConnector, PollConnector):
semantic_identifier=title,
doc_updated_at=company.updated_at.replace(tzinfo=timezone.utc),
metadata=metadata,
doc_metadata={
"hierarchy": {
"source_path": ["Companies"],
"object_type": "company",
"object_id": company.id,
}
},
)
)
@@ -738,6 +752,13 @@ class HubSpotConnector(LoadConnector, PollConnector):
semantic_identifier=title,
doc_updated_at=deal.updated_at.replace(tzinfo=timezone.utc),
metadata=metadata,
doc_metadata={
"hierarchy": {
"source_path": ["Deals"],
"object_type": "deal",
"object_id": deal.id,
}
},
)
)
@@ -881,6 +902,13 @@ class HubSpotConnector(LoadConnector, PollConnector):
semantic_identifier=title,
doc_updated_at=contact.updated_at.replace(tzinfo=timezone.utc),
metadata=metadata,
doc_metadata={
"hierarchy": {
"source_path": ["Contacts"],
"object_type": "contact",
"object_id": contact.id,
}
},
)
)

View File

@@ -274,6 +274,10 @@ class LinearConnector(LoadConnector, PollConnector, OAuthConnector):
# Cast the sections list to the expected type
typed_sections = cast(list[TextSection | ImageSection], sections)
# Extract team name for hierarchy
team_name = (node.get("team") or {}).get("name") or "Unknown Team"
identifier = node.get("identifier", node["id"])
documents.append(
Document(
id=node["id"],
@@ -282,6 +286,13 @@ class LinearConnector(LoadConnector, PollConnector, OAuthConnector):
semantic_identifier=f"[{node['identifier']}] {node['title']}",
title=node["title"],
doc_updated_at=time_str_to_utc(node["updatedAt"]),
doc_metadata={
"hierarchy": {
"source_path": [team_name],
"team_name": team_name,
"identifier": identifier,
}
},
metadata={
k: str(v)
for k, v in {

View File

@@ -234,6 +234,8 @@ def thread_to_doc(
"\n", " "
)
channel_name = channel["name"]
return Document(
id=_build_doc_id(channel_id=channel_id, thread_ts=thread[0]["ts"]),
sections=[
@@ -247,7 +249,14 @@ def thread_to_doc(
semantic_identifier=doc_sem_id,
doc_updated_at=get_latest_message_time(thread),
primary_owners=valid_experts,
metadata={"Channel": channel["name"]},
doc_metadata={
"hierarchy": {
"source_path": [channel_name],
"channel_name": channel_name,
"channel_id": channel_id,
}
},
metadata={"Channel": channel_name},
external_access=channel_access,
)

View File

@@ -22,6 +22,7 @@ from onyx.db.credentials import fetch_credential_by_id_for_user
from onyx.db.engine.sql_engine import get_session_with_current_tenant
from onyx.db.enums import AccessType
from onyx.db.enums import ConnectorCredentialPairStatus
from onyx.db.enums import ProcessingMode
from onyx.db.models import Connector
from onyx.db.models import ConnectorCredentialPair
from onyx.db.models import Credential
@@ -116,7 +117,14 @@ def get_connector_credential_pairs_for_user(
eager_load_user: bool = False,
order_by_desc: bool = False,
source: DocumentSource | None = None,
processing_mode: ProcessingMode | None = ProcessingMode.REGULAR,
) -> list[ConnectorCredentialPair]:
"""Get connector credential pairs for a user.
Args:
processing_mode: Filter by processing mode. Defaults to REGULAR to hide
FILE_SYSTEM connectors from standard admin UI. Pass None to get all.
"""
if eager_load_user:
assert (
eager_load_credential
@@ -142,6 +150,9 @@ def get_connector_credential_pairs_for_user(
if ids:
stmt = stmt.where(ConnectorCredentialPair.id.in_(ids))
if processing_mode is not None:
stmt = stmt.where(ConnectorCredentialPair.processing_mode == processing_mode)
if order_by_desc:
stmt = stmt.order_by(desc(ConnectorCredentialPair.id))
@@ -160,6 +171,7 @@ def get_connector_credential_pairs_for_user_parallel(
eager_load_user: bool = False,
order_by_desc: bool = False,
source: DocumentSource | None = None,
processing_mode: ProcessingMode | None = ProcessingMode.REGULAR,
) -> list[ConnectorCredentialPair]:
with get_session_with_current_tenant() as db_session:
return get_connector_credential_pairs_for_user(
@@ -172,6 +184,7 @@ def get_connector_credential_pairs_for_user_parallel(
eager_load_user=eager_load_user,
order_by_desc=order_by_desc,
source=source,
processing_mode=processing_mode,
)
@@ -501,6 +514,7 @@ def add_credential_to_connector(
initial_status: ConnectorCredentialPairStatus = ConnectorCredentialPairStatus.SCHEDULED,
last_successful_index_time: datetime | None = None,
seeding_flow: bool = False,
processing_mode: ProcessingMode = ProcessingMode.REGULAR,
) -> StatusResponse:
connector = fetch_connector_by_id(connector_id, db_session)
@@ -566,6 +580,7 @@ def add_credential_to_connector(
access_type=access_type,
auto_sync_options=auto_sync_options,
last_successful_index_time=last_successful_index_time,
processing_mode=processing_mode,
)
db_session.add(association)
db_session.flush() # make sure the association has an id

View File

@@ -56,6 +56,13 @@ class IndexingMode(str, PyEnum):
REINDEX = "reindex"
class ProcessingMode(str, PyEnum):
"""Determines how documents are processed after fetching."""
REGULAR = "regular" # Full pipeline: chunk → embed → Vespa
FILE_SYSTEM = "file_system" # Write to file system only
class SyncType(str, PyEnum):
DOCUMENT_SET = "document_set"
USER_GROUP = "user_group"
@@ -194,3 +201,34 @@ class SwitchoverType(str, PyEnum):
REINDEX = "reindex"
ACTIVE_ONLY = "active_only"
INSTANT = "instant"
# Onyx Build Mode Enums
class BuildSessionStatus(str, PyEnum):
ACTIVE = "active"
IDLE = "idle"
class SandboxStatus(str, PyEnum):
PROVISIONING = "provisioning"
RUNNING = "running"
IDLE = "idle"
TERMINATED = "terminated"
FAILED = "failed"
def is_active(self) -> bool:
"""Check if sandbox is in an active state (running or idle)."""
return self in (SandboxStatus.RUNNING, SandboxStatus.IDLE)
def is_terminal(self) -> bool:
"""Check if sandbox is in a terminal state."""
return self in (SandboxStatus.TERMINATED, SandboxStatus.FAILED)
class ArtifactType(str, PyEnum):
WEB_APP = "web_app"
PPTX = "pptx"
DOCX = "docx"
IMAGE = "image"
MARKDOWN = "markdown"
EXCEL = "excel"

View File

@@ -11,6 +11,7 @@ from typing_extensions import TypedDict # noreorder
from uuid import UUID
from pydantic import ValidationError
from sqlalchemy.dialects.postgresql import JSONB as PGJSONB
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from fastapi_users_db_sqlalchemy import SQLAlchemyBaseOAuthAccountTableUUID
@@ -55,8 +56,12 @@ from onyx.configs.constants import FileOrigin
from onyx.configs.constants import MessageType
from onyx.db.enums import (
AccessType,
ArtifactType,
BuildSessionStatus,
EmbeddingPrecision,
IndexingMode,
ProcessingMode,
SandboxStatus,
SyncType,
SyncStatus,
MCPAuthenticationType,
@@ -609,6 +614,16 @@ class ConnectorCredentialPair(Base):
Enum(IndexingMode, native_enum=False), nullable=True
)
# Determines how documents are processed after fetching:
# REGULAR: Full pipeline (chunk → embed → Vespa)
# FILE_SYSTEM: Write to file system only (for CLI agent sandbox)
processing_mode: Mapped[ProcessingMode] = mapped_column(
Enum(ProcessingMode, native_enum=False),
nullable=False,
default=ProcessingMode.REGULAR,
server_default="regular",
)
connector: Mapped["Connector"] = relationship(
"Connector", back_populates="credentials"
)
@@ -4142,3 +4157,202 @@ class TenantUsage(Base):
# Ensure only one row per window start (tenant_id is in the schema name)
UniqueConstraint("window_start", name="uq_tenant_usage_window"),
)
"""Tables related to Build Mode (CLI Agent Platform)"""
class BuildSession(Base):
"""Stores metadata about CLI agent build sessions."""
__tablename__ = "build_session"
id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True), primary_key=True, default=uuid4
)
user_id: Mapped[UUID | None] = mapped_column(
PGUUID(as_uuid=True), ForeignKey("user.id", ondelete="CASCADE"), nullable=True
)
name: Mapped[str | None] = mapped_column(String, nullable=True)
status: Mapped[BuildSessionStatus] = mapped_column(
Enum(BuildSessionStatus, native_enum=False, name="buildsessionstatus"),
nullable=False,
default=BuildSessionStatus.ACTIVE,
)
created_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
last_activity_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
nextjs_port: Mapped[int | None] = mapped_column(Integer, nullable=True)
# Relationships
user: Mapped[User | None] = relationship("User", foreign_keys=[user_id])
artifacts: Mapped[list["Artifact"]] = relationship(
"Artifact", back_populates="session", cascade="all, delete-orphan"
)
messages: Mapped[list["BuildMessage"]] = relationship(
"BuildMessage", back_populates="session", cascade="all, delete-orphan"
)
snapshots: Mapped[list["Snapshot"]] = relationship(
"Snapshot", back_populates="session", cascade="all, delete-orphan"
)
__table_args__ = (
Index("ix_build_session_user_created", "user_id", desc("created_at")),
Index("ix_build_session_status", "status"),
)
class Sandbox(Base):
"""Stores sandbox container metadata for users (one sandbox per user)."""
__tablename__ = "sandbox"
id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True), primary_key=True, default=uuid4
)
user_id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True),
ForeignKey("user.id", ondelete="CASCADE"),
nullable=False,
unique=True,
)
container_id: Mapped[str | None] = mapped_column(String, nullable=True)
status: Mapped[SandboxStatus] = mapped_column(
Enum(SandboxStatus, native_enum=False, name="sandboxstatus"),
nullable=False,
default=SandboxStatus.PROVISIONING,
)
created_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
last_heartbeat: Mapped[datetime.datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
# Relationships
user: Mapped[User] = relationship("User")
__table_args__ = (
Index("ix_sandbox_status", "status"),
Index("ix_sandbox_container_id", "container_id"),
)
class Artifact(Base):
"""Stores metadata about artifacts generated by CLI agents."""
__tablename__ = "artifact"
id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True), primary_key=True, default=uuid4
)
session_id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True),
ForeignKey("build_session.id", ondelete="CASCADE"),
nullable=False,
)
type: Mapped[ArtifactType] = mapped_column(
Enum(ArtifactType, native_enum=False, name="artifacttype"), nullable=False
)
# path of artifact in sandbox relative to outputs/
path: Mapped[str] = mapped_column(String, nullable=False)
name: Mapped[str] = mapped_column(String, nullable=False)
created_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
updated_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
# Relationships
session: Mapped[BuildSession] = relationship(
"BuildSession", back_populates="artifacts"
)
__table_args__ = (
Index("ix_artifact_session_created", "session_id", desc("created_at")),
Index("ix_artifact_type", "type"),
)
class Snapshot(Base):
"""Stores metadata about session output snapshots."""
__tablename__ = "snapshot"
id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True), primary_key=True, default=uuid4
)
session_id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True),
ForeignKey("build_session.id", ondelete="CASCADE"),
nullable=False,
)
storage_path: Mapped[str] = mapped_column(String, nullable=False)
size_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0)
created_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
# Relationships
session: Mapped[BuildSession] = relationship(
"BuildSession", back_populates="snapshots"
)
__table_args__ = (
Index("ix_snapshot_session_created", "session_id", desc("created_at")),
)
class BuildMessage(Base):
"""Stores messages exchanged in build sessions.
All message data is stored in message_metadata as JSON (the raw ACP packet).
The turn_index groups all assistant responses under the user prompt they respond to.
Packet types stored in message_metadata:
- user_message: {type: "user_message", content: {...}}
- agent_message: {type: "agent_message", content: {...}} (accumulated from chunks)
- agent_thought: {type: "agent_thought", content: {...}} (accumulated from chunks)
- tool_call_progress: {type: "tool_call_progress", status: "completed", ...} (only completed)
- agent_plan_update: {type: "agent_plan_update", entries: [...]} (upserted, latest only)
"""
__tablename__ = "build_message"
id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True), primary_key=True, default=uuid4
)
session_id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True),
ForeignKey("build_session.id", ondelete="CASCADE"),
nullable=False,
)
turn_index: Mapped[int] = mapped_column(Integer, nullable=False)
type: Mapped[MessageType] = mapped_column(
Enum(MessageType, native_enum=False, name="messagetype"), nullable=False
)
message_metadata: Mapped[dict[str, Any]] = mapped_column(PGJSONB, nullable=False)
created_at: Mapped[datetime.datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
# Relationships
session: Mapped[BuildSession] = relationship(
"BuildSession", back_populates="messages"
)
__table_args__ = (
Index(
"ix_build_message_session_turn", "session_id", "turn_index", "created_at"
),
)

View File

@@ -1,3 +1,4 @@
from onyx.configs.app_configs import DEV_MODE
from onyx.feature_flags.interface import FeatureFlagProvider
from onyx.feature_flags.interface import NoOpFeatureFlagProvider
from onyx.utils.variable_functionality import (
@@ -19,7 +20,7 @@ def get_default_feature_flag_provider() -> FeatureFlagProvider:
Returns:
FeatureFlagProvider: The configured feature flag provider instance
"""
if MULTI_TENANT:
if MULTI_TENANT or DEV_MODE:
return fetch_versioned_implementation_with_fallback(
module="onyx.feature_flags.factory",
attribute="get_posthog_feature_flag_provider",

View File

@@ -0,0 +1,168 @@
"""
Persistent Document Writer for writing indexed documents to local filesystem with
hierarchical directory structure that mirrors the source organization.
"""
import hashlib
import json
from pathlib import Path
from onyx.connectors.models import Document
from onyx.server.features.build.configs import PERSISTENT_DOCUMENT_STORAGE_PATH
from onyx.utils.logger import setup_logger
logger = setup_logger()
class PersistentDocumentWriter:
"""Writes indexed documents to local filesystem with hierarchical structure.
Documents are stored in user-segregated paths:
{base_path}/{user_id}/{source}/{hierarchy}/document.json
This enables per-user isolation for sandbox access control.
"""
def __init__(
self,
base_path: str,
user_id: str,
):
self.base_path = Path(base_path)
self.user_id = user_id
def write_documents(self, documents: list[Document]) -> list[str]:
"""Write documents to local filesystem, returns written file paths"""
written_paths = []
# Build a map of base filenames to detect duplicates
# Key: (directory_path, base_filename) -> list of docs with that name
filename_map: dict[tuple[Path, str], list[Document]] = {}
for doc in documents:
dir_path = self._build_directory_path(doc)
base_filename = self._get_base_filename(doc)
key = (dir_path, base_filename)
if key not in filename_map:
filename_map[key] = []
filename_map[key].append(doc)
# Now write documents, appending ID if there are duplicates
for (dir_path, base_filename), docs in filename_map.items():
has_duplicates = len(docs) > 1
for doc in docs:
try:
if has_duplicates:
# Append sanitized ID to disambiguate
id_suffix = self._sanitize_path_component(doc.id)
if len(id_suffix) > 50:
id_suffix = hashlib.sha256(doc.id.encode()).hexdigest()[:16]
filename = f"{base_filename}_{id_suffix}.json"
else:
filename = f"{base_filename}.json"
path = dir_path / filename
self._write_document(doc, path)
written_paths.append(str(path))
except Exception as e:
logger.warning(
f"Failed to write document {doc.id} to persistent storage: {e}"
)
return written_paths
def _build_directory_path(self, doc: Document) -> Path:
"""Build directory path from document metadata.
Documents are stored under user-segregated paths:
{base_path}/{user_id}/{source}/{hierarchy}/
This enables per-user isolation for sandbox access control.
"""
parts: list[str] = []
# Add user_id as the first path component for user segregation
parts.append(self.user_id)
parts.append(doc.source.value)
# Get hierarchy from doc_metadata
hierarchy = doc.doc_metadata.get("hierarchy", {}) if doc.doc_metadata else {}
source_path = hierarchy.get("source_path", [])
if source_path:
parts.extend([self._sanitize_path_component(p) for p in source_path])
return self.base_path / "/".join(parts)
def _get_base_filename(self, doc: Document) -> str:
"""Get base filename from semantic identifier, falling back to ID"""
# Prefer semantic_identifier, fall back to title, then ID
name = doc.semantic_identifier or doc.title or doc.id
return self._sanitize_filename(name)
def _sanitize_path_component(self, component: str) -> str:
"""Sanitize a path component for file system safety"""
# Replace spaces with underscores
sanitized = component.replace(" ", "_")
# Replace other problematic characters
sanitized = sanitized.replace("/", "_").replace("\\", "_").replace(":", "_")
sanitized = sanitized.replace("<", "_").replace(">", "_").replace("|", "_")
sanitized = sanitized.replace('"', "_").replace("?", "_").replace("*", "_")
# Also handle null bytes and other control characters
sanitized = "".join(c for c in sanitized if ord(c) >= 32)
return sanitized.strip() or "unnamed"
def _sanitize_filename(self, name: str) -> str:
"""Sanitize name for use as filename"""
sanitized = self._sanitize_path_component(name)
if len(sanitized) > 200:
# Keep first 150 chars + hash suffix for uniqueness
hash_suffix = hashlib.sha256(name.encode()).hexdigest()[:16]
return f"{sanitized[:150]}_{hash_suffix}"
return sanitized
def _write_document(self, doc: Document, path: Path) -> None:
"""Serialize and write document to filesystem"""
content = {
"id": doc.id,
"semantic_identifier": doc.semantic_identifier,
"title": doc.title,
"source": doc.source.value,
"doc_updated_at": (
doc.doc_updated_at.isoformat() if doc.doc_updated_at else None
),
"metadata": doc.metadata,
"doc_metadata": doc.doc_metadata,
"sections": [
{"text": s.text if hasattr(s, "text") else None, "link": s.link}
for s in doc.sections
],
"primary_owners": [o.model_dump() for o in (doc.primary_owners or [])],
"secondary_owners": [o.model_dump() for o in (doc.secondary_owners or [])],
}
# Create parent directories if they don't exist
path.parent.mkdir(parents=True, exist_ok=True)
# Write the JSON file
with open(path, "w", encoding="utf-8") as f:
json.dump(content, f, indent=2, default=str)
logger.debug(f"Wrote document to {path}")
def get_persistent_document_writer(
user_id: str,
) -> PersistentDocumentWriter:
"""Factory function to create a PersistentDocumentWriter with default configuration.
Args:
user_id: User ID for user-segregated storage paths.
Documents are stored under {base_path}/{user_id}/...
for sandbox access control isolation.
"""
return PersistentDocumentWriter(
base_path=PERSISTENT_DOCUMENT_STORAGE_PATH,
user_id=user_id,
)

View File

@@ -738,7 +738,7 @@ def model_is_reasoning_model(model_name: str, model_provider: str) -> bool:
# Fallback: try using litellm.supports_reasoning() for newer models
try:
logger.debug("Falling back to `litellm.supports_reasoning`")
# logger.debug("Falling back to `litellm.supports_reasoning`")
full_model_name = (
f"{model_provider}/{model_name}"
if model_provider not in model_name

View File

@@ -63,6 +63,8 @@ from onyx.server.documents.connector import router as connector_router
from onyx.server.documents.credential import router as credential_router
from onyx.server.documents.document import router as document_router
from onyx.server.documents.standard_oauth import router as standard_oauth_router
from onyx.server.features.build.api.api import nextjs_assets_router
from onyx.server.features.build.api.api import router as build_router
from onyx.server.features.default_assistant.api import (
router as default_assistant_router,
)
@@ -376,6 +378,8 @@ def get_application(lifespan_override: Lifespan | None = None) -> FastAPI:
include_router_with_global_prefix_prepended(application, admin_input_prompt_router)
include_router_with_global_prefix_prepended(application, cc_pair_router)
include_router_with_global_prefix_prepended(application, projects_router)
include_router_with_global_prefix_prepended(application, build_router)
include_router_with_global_prefix_prepended(application, nextjs_assets_router)
include_router_with_global_prefix_prepended(application, document_set_router)
include_router_with_global_prefix_prepended(application, search_settings_router)
include_router_with_global_prefix_prepended(

View File

@@ -564,6 +564,7 @@ def associate_credential_to_connector(
access_type=metadata.access_type,
auto_sync_options=metadata.auto_sync_options,
groups=metadata.groups,
processing_mode=metadata.processing_mode,
)
# trigger indexing immediately

View File

@@ -20,6 +20,7 @@ from google.oauth2.credentials import Credentials
from pydantic import BaseModel
from sqlalchemy.orm import Session
from onyx.auth.email_utils import send_email
from onyx.auth.users import current_admin_user
from onyx.auth.users import current_chat_accessible_user
from onyx.auth.users import current_curator_or_admin_user
@@ -29,6 +30,7 @@ from onyx.background.celery.tasks.pruning.tasks import (
)
from onyx.background.celery.versioned_apps.client import app as client_app
from onyx.configs.app_configs import DISABLE_AUTH
from onyx.configs.app_configs import EMAIL_CONFIGURED
from onyx.configs.app_configs import ENABLED_CONNECTOR_TYPES
from onyx.configs.app_configs import MOCK_CONNECTOR_FILE_PATH
from onyx.configs.constants import DocumentSource
@@ -125,6 +127,7 @@ from onyx.server.documents.models import ConnectorFileInfo
from onyx.server.documents.models import ConnectorFilesResponse
from onyx.server.documents.models import ConnectorIndexingStatusLite
from onyx.server.documents.models import ConnectorIndexingStatusLiteResponse
from onyx.server.documents.models import ConnectorRequestSubmission
from onyx.server.documents.models import ConnectorSnapshot
from onyx.server.documents.models import ConnectorStatus
from onyx.server.documents.models import ConnectorUpdateRequest
@@ -1759,6 +1762,86 @@ def get_connector_by_id(
)
@router.post("/connector-request")
def submit_connector_request(
request_data: ConnectorRequestSubmission,
user: User | None = Depends(current_user),
) -> StatusResponse:
"""
Submit a connector request for Cloud deployments.
Tracks via PostHog telemetry and sends email to hello@onyx.app.
"""
tenant_id = get_current_tenant_id()
connector_name = request_data.connector_name.strip()
if not connector_name:
raise HTTPException(status_code=400, detail="Connector name cannot be empty")
# Get user identifier for telemetry
user_email = user.email if user else None
distinct_id = user_email or tenant_id
# Track connector request via PostHog telemetry (Cloud only)
from shared_configs.configs import MULTI_TENANT
if MULTI_TENANT:
mt_cloud_telemetry(
tenant_id=tenant_id,
distinct_id=distinct_id,
event=MilestoneRecordType.REQUESTED_CONNECTOR,
properties={
"connector_name": connector_name,
"user_email": user_email,
},
)
# Send email notification (if email is configured)
if EMAIL_CONFIGURED:
try:
subject = "Onyx Craft Connector Request"
email_body_text = f"""A new connector request has been submitted:
Connector Name: {connector_name}
User Email: {user_email or 'Not provided (anonymous user)'}
Tenant ID: {tenant_id}
"""
email_body_html = f"""<html>
<body>
<p>A new connector request has been submitted:</p>
<ul>
<li><strong>Connector Name:</strong> {connector_name}</li>
<li><strong>User Email:</strong> {user_email or 'Not provided (anonymous user)'}</li>
<li><strong>Tenant ID:</strong> {tenant_id}</li>
</ul>
</body>
</html>"""
send_email(
user_email="hello@onyx.app",
subject=subject,
html_body=email_body_html,
text_body=email_body_text,
)
logger.info(
f"Connector request email sent to hello@onyx.app for connector: {connector_name}"
)
except Exception as e:
# Log error but don't fail the request if email fails
logger.error(
f"Failed to send connector request email for {connector_name}: {e}"
)
logger.info(
f"Connector request submitted: {connector_name} by user {user_email or 'anonymous'} "
f"(tenant: {tenant_id})"
)
return StatusResponse(
success=True,
message="Connector request submitted successfully. We'll prioritize popular requests!",
)
class BasicCCPairInfo(BaseModel):
has_successful_run: bool
source: DocumentSource

View File

@@ -18,6 +18,7 @@ from onyx.connectors.models import InputType
from onyx.db.enums import AccessType
from onyx.db.enums import ConnectorCredentialPairStatus
from onyx.db.enums import PermissionSyncStatus
from onyx.db.enums import ProcessingMode
from onyx.db.models import Connector
from onyx.db.models import ConnectorCredentialPair
from onyx.db.models import Credential
@@ -483,6 +484,7 @@ class ConnectorCredentialPairMetadata(BaseModel):
access_type: AccessType
auto_sync_options: dict[str, Any] | None = None
groups: list[int] = Field(default_factory=list)
processing_mode: ProcessingMode = ProcessingMode.REGULAR
class CCStatusUpdateRequest(BaseModel):
@@ -523,6 +525,10 @@ class RunConnectorRequest(BaseModel):
from_beginning: bool = False
class ConnectorRequestSubmission(BaseModel):
connector_name: str
class CCPropertyUpdateRequest(BaseModel):
name: str
value: str

View File

@@ -0,0 +1 @@
sandbox/kubernetes/docker/templates/outputs/venv

View File

@@ -0,0 +1,257 @@
# AGENTS.md
This file provides guidance for AI agents when working in this sandbox.
## Introduction
You are Steve, an AI agent powering **Onyx Craft**, a feature that allows users to create interactive web applications and dashboards from their company knowledge. You are running in a secure sandbox with access to the user's knowledge sources and the ability to create Next.js applications.
## Purpose
Your primary purpose is to assist users in accomplishing their goals by providing information, executing tasks, and offering guidance. I aim to be a reliable partner in problem-solving and task completion.
## How I Approach Tasks
When presented with a task, I typically:
1. Analyze the request to understand what's being asked
2. Break down complex problems into manageable steps
3. Use appropriate tools and methods to address each step
4. Provide clear communication throughout the process
5. Deliver results in a helpful and organized manner
## My Personality Traits
- Helpful and service-oriented
- Detail-focused and thorough
- Adaptable to different user needs
- Patient when working through complex problems
- Honest about my capabilities and limitations
## Areas I Can Help With
- Information gathering and research
- Knowledge Synthesis
- Data processing and analysis
- File management and organization
- Dashboard creation
- Repetitive administrative tasks
{{USER_CONTEXT}}
## Your Configuration
**LLM Provider**: {{LLM_PROVIDER_NAME}}
**Model**: {{LLM_MODEL_NAME}}
**Next.js Development Server**: Running on port {{NEXTJS_PORT}}
{{DISABLED_TOOLS_SECTION}}
## Your Environment
You are in an ephemeral virtual machine.
You currently have Python 3.11.13 and Node v22.21.1.
**Python Virtual Environment**: A Python virtual environment is pre-configured at `.venv/` with common data science and visualization packages already installed (numpy, pandas, matplotlib, scipy, PIL, etc.). The environment should be automatically activated, but if you run into issues with missing packages, you can explicitly use `.venv/bin/python` or `.venv/bin/pip`.
If you need additional packages, install them with `pip install <package>` (or `.venv/bin/pip install <package>` if the venv isn't active). For javascript packages, use `npm install <package>` from within the `outputs/web` directory.
## Organization Info
The `org_info/` directory contains information about the organization and user context:
- `AGENTS.md`: Description of available organizational information files
- `user_identity_profile.txt`: Contains the current user's name, email, and organization they work for. Use this information when personalizing outputs or when the user asks about their identity.
- `organization_structure.json`: Contains a JSON representation of the organization's groups, managers, and their direct reports. Use this to understand reporting relationships and team structures.
## Available Skills
{{AVAILABLE_SKILLS_SECTION}}
Skills contain best practices and guidelines for specific tasks. Always read the relevant skill's SKILL.md file BEFORE starting work that the skill covers.
## General Capabilities
### Information Processing
- Answering questions on diverse topics using available information
- Conducting research through web searches and data analysis
- Fact-checking and information verification from multiple sources
- Summarizing complex information into digestible formats
- Processing and analyzing structured and unstructured data
### Problem Solving
- Breaking down complex problems into manageable steps
- Providing step-by-step solutions to technical challenges
- Troubleshooting errors in code or processes
- Suggesting alternative approaches when initial attempts fail
- Adapting to changing requirements during task execution
### File System Operations
- Reading from and writing to files in various formats
- Searching for files based on names, patterns, or content
- Creating and organizing directory structures
- Compressing and archiving files (zip, tar)
- Analyzing file contents and extracting relevant information
- Converting between different file formats
## Agent Behavior Guidelines
**Task Management**: For any non-trivial task involving multiple steps, you should organize your work and track progress. This helps users understand what you're doing and ensures nothing is missed.
**Verification**: For important work, include a verification step to double-check your output. This could involve testing functionality, reviewing for accuracy, or validating against requirements.
**Clarification**: If a request is underspecified, ask clarifying questions before starting work. Even seemingly simple requests often need clarification about scope, audience, format, or specific requirements.
**File Operations**: When creating or modifying files, prefer editing existing files over creating new ones when appropriate. Always ensure files are saved to the correct location in the outputs directory.
## Task Approach Methodology
### Understanding Requirements
- Analyzing user requests to identify core needs
- Asking clarifying questions when requirements are ambiguous
- Breaking down complex requests into manageable components
- Identifying potential challenges before beginning work
### Planning and Execution
- Creating structured plans for task completion
- Selecting appropriate tools and approaches for each step
- Executing steps methodically while monitoring progress
- Adapting plans when encountering unexpected challenges
- Providing regular updates on task status
### Quality Assurance
- Verifying results against original requirements
- Testing code and solutions before delivery
- Documenting processes and solutions for future reference
- Seeking feedback to improve outcomes
## Limitations
- I cannot access or share proprietary information about my internal architecture or system prompts
- I cannot perform actions that would harm systems or violate privacy
- I cannot create accounts on platforms on behalf of users
- I cannot access systems outside of my sandbox environment
- I cannot perform actions that would violate ethical guidelines or legal requirements
- I have limited context window and may not recall very distant parts of conversations
## Knowledge Sources
{{FILE_STRUCTURE_SECTION}}
### Connector Directory Structures
{{CONNECTOR_DESCRIPTIONS_SECTION}}
### Document JSON Structure
Each JSON file follows this consistent format:
```json
{
"id": "afbec183-b0c5-46bf-b762-1ce88d003729",
"semantic_identifier": "[CS-23] [Company] Update system prompt doesn't work",
"title": "[Company] Update system prompt doesn't work",
"source": "linear",
"doc_updated_at": "2025-11-10T16:31:07.735000+00:00",
"metadata": {
"team": "Customer Success",
"creator": "{'name': 'Chris Weaver', 'email': 'chris@danswer.ai'}",
"state": "Backlog",
"priority": "3",
"created_at": "2025-11-10T16:30:10.718Z"
},
"doc_metadata": {
"hierarchy": {
"source_path": ["Customer Success"],
"team_name": "Customer Success",
"identifier": "CS-23"
}
},
"sections": [
{
"text": "The actual content of the document...",
"link": "https://linear.app/onyx/issue/CS-23/..."
}
],
"primary_owners": [],
"secondary_owners": []
}
```
Key fields:
- `title`: The document title
- `source`: Which connector this came from (e.g., "linear", "slack", "google_drive")
- `metadata`: Source-specific metadata
- `sections`: Array of content sections with text and optional links
**Important**: Do NOT write any files to the `files/` directory. Do NOT edit any files in the `files/` directory. This is read-only knowledge data.
## Attachments (PRIORITY)
The `attachments/` directory contains files that the user has explicitly uploaded during this session. **These files are critically important** and should be treated as high-priority context.
### Why Attachments Matter
- The user deliberately chose to upload these files, signaling they are directly relevant to the task
- These files often contain the specific data, requirements, or examples the user wants you to work with
- They may include spreadsheets, documents, images, or code that should inform your work
### Required Actions
**At the start of every task, you MUST:**
1. **Check for attachments**: List the contents of `attachments/` to see what the user has provided
2. **Read and analyze each file**: Thoroughly examine every attachment to understand its contents and relevance
3. **Reference attachment content**: Use the information from attachments to inform your responses and outputs
### File Handling
- Uploaded files may be in various formats: CSV, JSON, PDF, images, text files, etc.
- For spreadsheets and data files, examine the structure, columns, and sample data
- For documents, extract key information and requirements
- For images, analyze and describe their content
- For code files, understand the logic and patterns
**Do NOT ignore user uploaded files.** They are there for a reason and likely contain exactly what you need to complete the task successfully.
## Outputs Directory
There is a special folder called `outputs`. Any and all python scripts, javascript apps, generated documents, slides, etc. should go here.
Feel free to write/edit anything you find in here.
## Outputs
There should be four main types of outputs:
1. Web Applications / Dashboards
Generally, you should use
### Web Applications / Dashboards
Web applications and dashboards should be written as a webapp built with Next.js, React, and shadcn/ui.. Within the `outputs` directory,
there is a folder called `web` that has the skeleton of a basic Next.js app in it. Use this. We do NOT use a `src` directory.
Use NextJS 16.1.1, React v19, Tailwindcss, and recharts.
The Next.js app is already running on port {{NEXTJS_PORT}}. Do not run `npm run dev` yourself.
If the app needs any pre-computation, then create a bash script called `prepare.sh` at the root of the `web` directory.
**IMPORTANT: See `outputs/web/AGENTS.md` for detailed technical specifications, architecture patterns, component usage guidelines, and styling rules. It is the ground truth for webapp design**
### Other Output Formats (Coming Soon)
Additional output formats such as slides, markdown documents, and standalone graphs are coming soon. If the user requests these formats, let them know they're not yet available and suggest building an interactive web application instead, which can include:
- Data visualizations and charts using recharts
- Multi-page layouts with navigation
- Exportable content (print-to-PDF functionality)
- Interactive dashboards with real-time filtering and sorting

View File

@@ -0,0 +1,114 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Structure
The `files` directory contains all of the knowledge from Chris' company, Onyx. This knowledge comes from Google Drive, Linear, Slack, Github, and Fireflies.
Each source has it's own directory - `Google_Drive`, `Linear`, `Slack`, `Github`, and `Fireflies`. Within each directory, the structure of the source is built out as a folder structure:
- Google Drive is copied over directly as is. End files are stored as `FILE_NAME.json`.
- Linear has each project as a folder, and then within each project, each individual ticket is stored as a file: `[TICKET_ID]_TICKET_NAME.json`.
- Slack has each channel as a folder titled `[CHANNEL_NAME]` in the root directory. Within each channel, each thread is represented as a single file called `[INITIAL_AUTHOR]_in_[CHANNEL]__[FIRST_MESSAGE].json`.
- Github has each organization as a folder titled `[ORG_NAME]`. Within each organization, there is
a folder for each repository tilted `[REPO_NAME]`. Within each repository there are up to two folders: `pull_requests` and `issues`. Each pull request / issue is then represented as a single file
within the appropriate folder. Pull requests are structured as `[PR_ID]__[PR_NAME].json` and issues
are structured as `[ISSUE_ID]__[ISSUE_NAME].json`.
- Fireflies has all calls in the root, each as a single file titled `CALL_TITLE.json`.
- HubSpot has four folders in the root: `Tickets`, `Companies`, `Deals`, and `Contacts`. Each object is stored as a file named after its title/name (e.g., `[TICKET_SUBJECT].json`, `[COMPANY_NAME].json`, `[DEAL_NAME].json`, `[CONTACT_NAME].json`).
Across all names, spaces are replaced by `_`.
Each JSON is structured like:
```
{
"id": "afbec183-b0c5-46bf-b768-1ce88d003729",
"semantic_identifier": "[CS-17] [Betclic] Update system prompt doesn't work",
"title": "[Betclic] Update system prompt doesn't work",
"source": "linear",
"doc_updated_at": "2025-11-10T16:31:07.735000+00:00",
"metadata": {
"team": "Customer Success",
"creator": "{'name': 'Chris Weaver', 'email': 'chris@danswer.ai'}",
"state": "Backlog",
"priority": "3",
"created_at": "2025-11-10T16:30:10.718Z"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"Customer Success"
],
"team_name": "Customer Success",
"identifier": "CS-17"
}
},
"sections": [
{
"text": "Happens \\~15% of the time.",
"link": "https://linear.app/onyx-app/issue/CS-17/betclic-update-system-prompt-doesnt-work"
}
],
"primary_owners": [],
"secondary_owners": []
}
```
Do NOT write any files to these directories. Do NOT edit any files in these directories.
There is a special folder called `outputs`. Any and all python scripts, javascript apps, generated documents, slides, etc. should go here.
Feel free to write/edit anything you find in here.
## Outputs
There should be four main types of outputs:
1. Web Applications / Dashboards
2. Slides
3. Markdown Documents
4. Graphs/Charts
Generally, you should use
### Web Applications / Dashboards
Web applications and dashboards should be written as a Next.js app. Within the `outputs` directory,
there is a folder called `web` that has the skeleton of a basic Next.js app in it. Use this.
Use NextJS 16.1.1, React v19, Tailwindcss, and recharts.
The Next.js app is already running and accessible at http://localhost:3002. Do not run `npm run dev` yourself.
If the app needs any pre-computation, then create a bash script called `prepare.sh` at the root of the `web` directory.
### Slides
Slides should be created using the nano-banana MCP.
The outputs should be placed within the `outputs/slides` directory, named `[SLIDE_NUMBER].png`.
Before creating slides, create a `SLIDE_OUTLINE.md` file describing the overall message as well as the content and structure of each slide.
### Markdown Documents
Markdown documents should be placed within the `outputs/document` directory.
If you want to have a single "Document" that has multiple distinct pages, then create a folder within
the `outputs/document` directory, and name each page `1.MD`, `2.MD`, ...
### Graphs/Charts
Graphs and charts should be placed in the `outputs/charts` directory.
Graphs and charts should be created with a python script. You have access to libraries like numpy, pandas, scipy, matplotlib, and PIL.
## Your Environment
You are in an ephemeral virtual machine.
You currently have Python 3.11.13 and Node v22.21.1.
**Python Virtual Environment**: A Python virtual environment is pre-configured at `.venv/` with common data science and visualization packages already installed (numpy, pandas, matplotlib, scipy, PIL, etc.). The environment should be automatically activated, but if you run into issues with missing packages, you can explicitly use `.venv/bin/python` or `.venv/bin/pip`.
If you need additional packages, install them with `pip install <package>` (or `.venv/bin/pip install <package>` if the venv isn't active). For javascript packages, use `npm` from within the `outputs/web` directory.

View File

@@ -0,0 +1 @@
# Build feature module

View File

@@ -0,0 +1,435 @@
from collections.abc import Iterator
from uuid import UUID
import httpx
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from fastapi import Request
from fastapi import Response
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
from onyx.auth.users import current_user
from onyx.configs.constants import DocumentSource
from onyx.configs.constants import NotificationType
from onyx.db.connector_credential_pair import get_connector_credential_pairs_for_user
from onyx.db.engine.sql_engine import get_session
from onyx.db.enums import ConnectorCredentialPairStatus
from onyx.db.enums import IndexingStatus
from onyx.db.enums import ProcessingMode
from onyx.db.index_attempt import get_latest_index_attempt_for_cc_pair_id
from onyx.db.models import User
from onyx.db.notification import get_notifications
from onyx.server.features.build.api.messages_api import router as messages_router
from onyx.server.features.build.api.models import BuildConnectorInfo
from onyx.server.features.build.api.models import BuildConnectorListResponse
from onyx.server.features.build.api.models import BuildConnectorStatus
from onyx.server.features.build.api.models import RateLimitResponse
from onyx.server.features.build.api.rate_limit import get_user_rate_limit_status
from onyx.server.features.build.api.sessions_api import router as sessions_router
from onyx.server.features.build.session.manager import SessionManager
from onyx.utils.logger import setup_logger
logger = setup_logger()
router = APIRouter(prefix="/build")
# Include sub-routers for sessions and messages
router.include_router(sessions_router, tags=["build"])
router.include_router(messages_router, tags=["build"])
# -----------------------------------------------------------------------------
# Rate Limiting
# -----------------------------------------------------------------------------
@router.get("/limit", response_model=RateLimitResponse)
def get_rate_limit(
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> RateLimitResponse:
"""Get rate limit information for the current user."""
return get_user_rate_limit_status(user, db_session)
# -----------------------------------------------------------------------------
# Build Connectors
# -----------------------------------------------------------------------------
@router.get("/connectors", response_model=BuildConnectorListResponse)
def get_build_connectors(
user: User | None = Depends(current_user),
db_session: Session = Depends(get_session),
) -> BuildConnectorListResponse:
"""Get all connectors for the build admin panel.
Returns all connector-credential pairs with simplified status information.
"""
cc_pairs = get_connector_credential_pairs_for_user(
db_session=db_session,
user=user,
get_editable=False,
eager_load_connector=True,
eager_load_credential=True,
processing_mode=ProcessingMode.FILE_SYSTEM, # Only show FILE_SYSTEM connectors
)
connectors: list[BuildConnectorInfo] = []
for cc_pair in cc_pairs:
# Skip ingestion API connectors and default pairs
if cc_pair.connector.source == DocumentSource.INGESTION_API:
continue
if cc_pair.name == "DefaultCCPair":
continue
# Determine status
error_message: str | None = None
if cc_pair.status == ConnectorCredentialPairStatus.DELETING:
status = BuildConnectorStatus.DELETING
elif cc_pair.status == ConnectorCredentialPairStatus.INVALID:
status = BuildConnectorStatus.ERROR
error_message = "Connector credentials are invalid"
else:
# Check latest index attempt for errors
latest_attempt = get_latest_index_attempt_for_cc_pair_id(
db_session=db_session,
connector_credential_pair_id=cc_pair.id,
secondary_index=False,
only_finished=True,
)
if latest_attempt and latest_attempt.status == IndexingStatus.FAILED:
status = BuildConnectorStatus.ERROR
error_message = latest_attempt.error_msg
elif (
latest_attempt
and latest_attempt.status == IndexingStatus.COMPLETED_WITH_ERRORS
):
status = BuildConnectorStatus.ERROR
error_message = "Indexing completed with errors"
elif cc_pair.status == ConnectorCredentialPairStatus.PAUSED:
status = BuildConnectorStatus.CONNECTED
elif cc_pair.last_successful_index_time is None:
# Never successfully indexed - check if currently indexing
# First check cc_pair status for scheduled/initial indexing
if cc_pair.status in (
ConnectorCredentialPairStatus.SCHEDULED,
ConnectorCredentialPairStatus.INITIAL_INDEXING,
):
status = BuildConnectorStatus.INDEXING
else:
in_progress_attempt = get_latest_index_attempt_for_cc_pair_id(
db_session=db_session,
connector_credential_pair_id=cc_pair.id,
secondary_index=False,
only_finished=False,
)
if (
in_progress_attempt
and in_progress_attempt.status == IndexingStatus.IN_PROGRESS
):
status = BuildConnectorStatus.INDEXING
elif (
in_progress_attempt
and in_progress_attempt.status == IndexingStatus.NOT_STARTED
):
status = BuildConnectorStatus.INDEXING
else:
# Has a finished attempt but never succeeded - likely error
status = BuildConnectorStatus.ERROR
error_message = (
latest_attempt.error_msg
if latest_attempt
else "Initial indexing failed"
)
else:
status = BuildConnectorStatus.CONNECTED
connectors.append(
BuildConnectorInfo(
cc_pair_id=cc_pair.id,
connector_id=cc_pair.connector.id,
credential_id=cc_pair.credential.id,
source=cc_pair.connector.source.value,
name=cc_pair.name or cc_pair.connector.name or "Unnamed",
status=status,
docs_indexed=0, # Would need to query for this
last_indexed=cc_pair.last_successful_index_time,
error_message=error_message,
)
)
return BuildConnectorListResponse(connectors=connectors)
# Headers to skip when proxying (hop-by-hop headers)
EXCLUDED_HEADERS = {
"content-encoding",
"content-length",
"transfer-encoding",
"connection",
}
def _stream_response(response: httpx.Response) -> Iterator[bytes]:
"""Stream the response content in chunks."""
for chunk in response.iter_bytes(chunk_size=8192):
yield chunk
def _rewrite_asset_paths(content: bytes, session_id: str) -> bytes:
"""Rewrite Next.js asset paths to go through the proxy."""
import re
# Base path includes session_id for routing
webapp_base_path = f"/api/build/sessions/{session_id}/webapp"
text = content.decode("utf-8")
# Rewrite /_next/ paths to go through our proxy
text = text.replace("/_next/", f"{webapp_base_path}/_next/")
# Rewrite root-level JSON data file fetch paths (e.g., /data.json, /pr_data.json)
# Only matches paths like "/filename.json" (no subdirectories)
text = re.sub(r'"(/[a-zA-Z0-9_-]+\.json)"', f'"{webapp_base_path}\\1"', text)
text = re.sub(r"'(/[a-zA-Z0-9_-]+\.json)'", f"'{webapp_base_path}\\1'", text)
# Rewrite favicon
text = text.replace('"/favicon.ico', f'"{webapp_base_path}/favicon.ico')
return text.encode("utf-8")
# Content types that may contain asset path references that need rewriting
REWRITABLE_CONTENT_TYPES = {
"text/html",
"text/css",
"application/javascript",
"text/javascript",
"application/x-javascript",
}
def _get_sandbox_url(session_id: UUID, db_session: Session) -> str:
"""Get the localhost URL for a session's Next.js server.
Args:
session_id: The build session ID
db_session: Database session
Returns:
The localhost URL (e.g., "http://localhost:3010")
Raises:
HTTPException: If session not found or port not allocated
"""
from onyx.db.models import BuildSession
session = db_session.get(BuildSession, session_id)
if not session:
raise HTTPException(status_code=404, detail="Session not found")
if session.nextjs_port is None:
raise HTTPException(status_code=503, detail="Session port not allocated")
return f"http://localhost:{session.nextjs_port}"
def _proxy_request(
path: str, request: Request, session_id: UUID, db_session: Session
) -> StreamingResponse | Response:
"""Proxy a request to the sandbox's Next.js server."""
base_url = _get_sandbox_url(session_id, db_session)
# Build the target URL
target_url = f"{base_url}/{path.lstrip('/')}"
# Include query params if present
if request.query_params:
target_url = f"{target_url}?{request.query_params}"
logger.debug(f"Proxying request to: {target_url}")
try:
# Make the request to the target URL
with httpx.Client(timeout=30.0, follow_redirects=True) as client:
response = client.get(
target_url,
headers={
key: value
for key, value in request.headers.items()
if key.lower() not in ("host", "content-length")
},
)
# Build response headers, excluding hop-by-hop headers
response_headers = {
key: value
for key, value in response.headers.items()
if key.lower() not in EXCLUDED_HEADERS
}
content_type = response.headers.get("content-type", "")
# For HTML/CSS/JS responses, rewrite asset paths
if any(ct in content_type for ct in REWRITABLE_CONTENT_TYPES):
content = _rewrite_asset_paths(response.content, str(session_id))
return Response(
content=content,
status_code=response.status_code,
headers=response_headers,
media_type=content_type,
)
return StreamingResponse(
content=_stream_response(response),
status_code=response.status_code,
headers=response_headers,
media_type=content_type or None,
)
except httpx.TimeoutException:
logger.error(f"Timeout while proxying request to {target_url}")
raise HTTPException(status_code=504, detail="Gateway timeout")
except httpx.RequestError as e:
logger.error(f"Error proxying request to {target_url}: {e}")
raise HTTPException(status_code=502, detail="Bad gateway")
@router.get("/sessions/{session_id}/webapp", response_model=None)
def get_webapp_root(
session_id: UUID,
request: Request,
_: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> StreamingResponse | Response:
"""Proxy the root path of the webapp for a specific session."""
return _proxy_request("", request, session_id, db_session)
@router.get("/sessions/{session_id}/webapp/{path:path}", response_model=None)
def get_webapp_path(
session_id: UUID,
path: str,
request: Request,
_: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> StreamingResponse | Response:
"""Proxy any subpath of the webapp (static assets, etc.) for a specific session."""
return _proxy_request(path, request, session_id, db_session)
# -----------------------------------------------------------------------------
# Development Endpoints
# -----------------------------------------------------------------------------
@router.post("/dev/clear-feature-announcements-and-logout")
def clear_feature_announcements_and_logout(
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> dict[str, str]:
"""
[DEV] Delete all FEATURE_ANNOUNCEMENT notifications for the current user from the database.
This is a temporary development endpoint for testing purposes.
The frontend should call logout after this endpoint succeeds.
"""
# Get all FEATURE_ANNOUNCEMENT notifications for the current user (including dismissed)
feature_announcements = get_notifications(
user=user,
db_session=db_session,
notif_type=NotificationType.FEATURE_ANNOUNCEMENT,
include_dismissed=True,
)
# Delete all of them from the database
if feature_announcements:
for notification in feature_announcements:
db_session.delete(notification)
db_session.commit()
return {"status": "success", "message": "Notifications deleted"}
# Separate router for Next.js static assets at /_next/*
# This is needed because Next.js apps may reference assets with root-relative paths
# that don't get rewritten. The session_id is extracted from the Referer header.
nextjs_assets_router = APIRouter()
def _extract_session_from_referer(request: Request) -> UUID | None:
"""Extract session_id from the Referer header.
Expects Referer to contain /api/build/sessions/{session_id}/webapp
"""
import re
referer = request.headers.get("referer", "")
match = re.search(r"/api/build/sessions/([a-f0-9-]+)/webapp", referer)
if match:
try:
return UUID(match.group(1))
except ValueError:
return None
return None
@nextjs_assets_router.get("/_next/{path:path}", response_model=None)
def get_nextjs_assets(
path: str,
request: Request,
_: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> StreamingResponse | Response:
"""Proxy Next.js static assets requested at root /_next/ path.
The session_id is extracted from the Referer header since these requests
come from within the iframe context.
"""
session_id = _extract_session_from_referer(request)
if not session_id:
raise HTTPException(
status_code=400,
detail="Could not determine session from request context",
)
return _proxy_request(f"_next/{path}", request, session_id, db_session)
# =============================================================================
# Sandbox Management Endpoints
# =============================================================================
@router.post("/sandbox/reset", response_model=None)
def reset_sandbox(
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> Response:
"""Reset the user's sandbox by terminating it and cleaning up all sessions.
This endpoint terminates the user's shared sandbox container/pod and
cleans up all session workspaces. Useful for "start fresh" functionality.
After calling this endpoint, the next session creation will provision a
new sandbox.
"""
session_manager = SessionManager(db_session)
try:
success = session_manager.terminate_user_sandbox(user.id)
if not success:
raise HTTPException(
status_code=404,
detail="No sandbox found for user",
)
db_session.commit()
except HTTPException:
raise
except Exception as e:
db_session.rollback()
logger.error(f"Failed to reset sandbox for user {user.id}: {e}")
raise HTTPException(
status_code=500,
detail=f"Failed to reset sandbox: {e}",
)
return Response(status_code=204)

View File

@@ -0,0 +1,98 @@
"""API endpoints for Build Mode message management."""
from uuid import UUID
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
from onyx.auth.users import current_user
from onyx.configs.constants import PUBLIC_API_TAGS
from onyx.db.engine.sql_engine import get_session
from onyx.db.models import User
from onyx.server.features.build.api.models import MessageListResponse
from onyx.server.features.build.api.models import MessageRequest
from onyx.server.features.build.api.models import MessageResponse
from onyx.server.features.build.session.manager import RateLimitError
from onyx.server.features.build.session.manager import SessionManager
from onyx.utils.logger import setup_logger
logger = setup_logger()
router = APIRouter()
def check_build_rate_limits(
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> None:
"""
Dependency to check build mode rate limits before processing the request.
Raises HTTPException(429) if rate limit is exceeded.
Follows the same pattern as chat's check_token_rate_limits.
"""
session_manager = SessionManager(db_session)
try:
session_manager.check_rate_limit(user)
except RateLimitError as e:
raise HTTPException(
status_code=429,
detail=str(e),
)
@router.get("/sessions/{session_id}/messages", tags=PUBLIC_API_TAGS)
def list_messages(
session_id: UUID,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> MessageListResponse:
"""Get all messages for a build session."""
if user is None:
raise HTTPException(status_code=401, detail="Authentication required")
session_manager = SessionManager(db_session)
messages = session_manager.list_messages(session_id, user.id)
if messages is None:
raise HTTPException(status_code=404, detail="Session not found")
return MessageListResponse(
messages=[MessageResponse.from_model(msg) for msg in messages]
)
@router.post("/sessions/{session_id}/send-message", tags=PUBLIC_API_TAGS)
async def send_message(
session_id: UUID,
request: MessageRequest,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
_rate_limit_check: None = Depends(check_build_rate_limits),
) -> StreamingResponse:
"""
Send a message to the CLI agent and stream the response.
Enforces rate limiting before executing the agent (via dependency).
Returns a Server-Sent Events (SSE) stream with the agent's response.
Follows the same pattern as /chat/send-message for consistency.
"""
session_manager = SessionManager(db_session)
# Stream the CLI agent's response
return StreamingResponse(
session_manager.send_message(session_id, user.id, request.content),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no", # Disable nginx buffering
},
)

View File

@@ -0,0 +1,272 @@
from datetime import datetime
from enum import Enum
from typing import Any
from pydantic import BaseModel
from onyx.configs.constants import MessageType
from onyx.db.enums import ArtifactType
from onyx.db.enums import BuildSessionStatus
from onyx.db.enums import SandboxStatus
# ===== Session Models =====
class SessionCreateRequest(BaseModel):
"""Request to create a new build session."""
name: str | None = None # Optional session name
demo_data_enabled: bool = True # Whether to enable demo org_info data in sandbox
user_work_area: str | None = None # User's work area (e.g., "engineering")
user_level: str | None = None # User's level (e.g., "ic", "manager")
# LLM selection from user's cookie
llm_provider_type: str | None = None # Provider type (e.g., "anthropic", "openai")
llm_model_name: str | None = None # Model name (e.g., "claude-opus-4-5")
class SessionUpdateRequest(BaseModel):
"""Request to update a build session.
If name is None, the session name will be auto-generated using LLM.
"""
name: str | None = None
class SessionNameGenerateResponse(BaseModel):
"""Response containing a generated session name."""
name: str
class SandboxResponse(BaseModel):
"""Sandbox metadata in session response."""
id: str
status: SandboxStatus
container_id: str | None
created_at: datetime
last_heartbeat: datetime | None
@classmethod
def from_model(cls, sandbox: Any) -> "SandboxResponse":
"""Convert Sandbox ORM model to response."""
return cls(
id=str(sandbox.id),
status=sandbox.status,
container_id=sandbox.container_id,
created_at=sandbox.created_at,
last_heartbeat=sandbox.last_heartbeat,
)
class ArtifactResponse(BaseModel):
"""Artifact metadata in session response."""
id: str
session_id: str
type: ArtifactType
name: str
path: str
preview_url: str | None
created_at: datetime
updated_at: datetime
@classmethod
def from_model(cls, artifact: Any) -> "ArtifactResponse":
"""Convert Artifact ORM model to response."""
return cls(
id=str(artifact.id),
session_id=str(artifact.session_id),
type=artifact.type,
name=artifact.name,
path=artifact.path,
preview_url=getattr(artifact, "preview_url", None),
created_at=artifact.created_at,
updated_at=artifact.updated_at,
)
class SessionResponse(BaseModel):
"""Response containing session details."""
id: str
user_id: str | None
name: str | None
status: BuildSessionStatus
created_at: datetime
last_activity_at: datetime
nextjs_port: int | None
sandbox: SandboxResponse | None
artifacts: list[ArtifactResponse]
@classmethod
def from_model(cls, session: Any, sandbox: Any | None = None) -> "SessionResponse":
"""Convert BuildSession ORM model to response.
Args:
session: BuildSession ORM model
sandbox: Optional Sandbox ORM model. Since sandboxes are now user-owned
(not session-owned), the sandbox must be passed separately.
"""
return cls(
id=str(session.id),
user_id=str(session.user_id) if session.user_id else None,
name=session.name,
status=session.status,
created_at=session.created_at,
last_activity_at=session.last_activity_at,
nextjs_port=session.nextjs_port,
sandbox=(SandboxResponse.from_model(sandbox) if sandbox else None),
artifacts=[ArtifactResponse.from_model(a) for a in session.artifacts],
)
class SessionListResponse(BaseModel):
"""Response containing list of sessions."""
sessions: list[SessionResponse]
# ===== Message Models =====
class MessageRequest(BaseModel):
"""Request to send a message to the CLI agent."""
content: str
class MessageResponse(BaseModel):
"""Response containing message details.
All message data is stored in message_metadata as JSON (the raw ACP packet).
The turn_index groups all assistant responses under the user prompt they respond to.
Packet types in message_metadata:
- user_message: {type: "user_message", content: {...}}
- agent_message: {type: "agent_message", content: {...}}
- agent_thought: {type: "agent_thought", content: {...}}
- tool_call_progress: {type: "tool_call_progress", status: "completed", ...}
- agent_plan_update: {type: "agent_plan_update", entries: [...]}
"""
id: str
session_id: str
turn_index: int
type: MessageType
message_metadata: dict[str, Any]
created_at: datetime
@classmethod
def from_model(cls, message: Any) -> "MessageResponse":
"""Convert BuildMessage ORM model to response."""
return cls(
id=str(message.id),
session_id=str(message.session_id),
turn_index=message.turn_index,
type=message.type,
message_metadata=message.message_metadata,
created_at=message.created_at,
)
class MessageListResponse(BaseModel):
"""Response containing list of messages."""
messages: list[MessageResponse]
# ===== Legacy Models (for compatibility with other code) =====
class CreateSessionRequest(BaseModel):
task: str
available_sources: list[str] | None = None
class CreateSessionResponse(BaseModel):
session_id: str
class ExecuteRequest(BaseModel):
task: str
context: str | None = None
class ArtifactInfo(BaseModel):
artifact_type: str # "webapp", "file", "markdown", "image"
path: str
filename: str
mime_type: str | None = None
class SessionStatus(BaseModel):
session_id: str
status: str # "idle", "running", "completed", "failed"
webapp_url: str | None = None
class FileSystemEntry(BaseModel):
name: str # File/folder name
path: str # Relative path from sandbox root
is_directory: bool # True for folders
size: int | None = None # File size in bytes
mime_type: str | None = None # MIME type for files
class DirectoryListing(BaseModel):
path: str # Current directory path
entries: list[FileSystemEntry] # Contents
class WebappInfo(BaseModel):
has_webapp: bool # Whether a webapp exists in outputs/web
webapp_url: str | None # URL to access the webapp (e.g., http://localhost:3015)
status: str # Sandbox status (running, terminated, etc.)
# ===== File Upload Models =====
class UploadResponse(BaseModel):
"""Response after successful file upload."""
filename: str # Sanitized filename
path: str # Relative path in sandbox (e.g., "attachments/doc.pdf")
size_bytes: int # File size in bytes
# ===== Rate Limit Models =====
class RateLimitResponse(BaseModel):
"""Rate limit information."""
is_limited: bool
limit_type: str # "weekly" or "total"
messages_used: int
limit: int
reset_timestamp: str | None = None
# ===== Build Connector Models =====
class BuildConnectorStatus(str, Enum):
"""Status of a build connector."""
NOT_CONNECTED = "not_connected"
CONNECTED = "connected"
INDEXING = "indexing"
ERROR = "error"
DELETING = "deleting"
class BuildConnectorInfo(BaseModel):
"""Simplified connector info for build admin panel."""
cc_pair_id: int
connector_id: int
credential_id: int
source: str
name: str
status: BuildConnectorStatus
docs_indexed: int
last_indexed: datetime | None
error_message: str | None = None
class BuildConnectorListResponse(BaseModel):
"""List of build connectors."""
connectors: list[BuildConnectorInfo]

View File

@@ -0,0 +1,101 @@
"""Simple packet logger for build mode debugging.
Logs the raw JSON of every packet emitted during build mode.
Log output: backend/onyx/server/features/build/packets.log
"""
import json
import logging
import os
from pathlib import Path
from typing import Any
class PacketLogger:
"""Simple packet logger - outputs raw JSON for each packet."""
_instance: "PacketLogger | None" = None
_initialized: bool
def __new__(cls) -> "PacketLogger":
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self) -> None:
if self._initialized:
return
self._initialized = True
self._enabled = os.getenv("LOG_LEVEL", "").upper() == "DEBUG"
self._logger: logging.Logger | None = None
if self._enabled:
self._setup_logger()
def _setup_logger(self) -> None:
"""Set up the file handler for packet logging."""
# Log to backend/onyx/server/features/build/packets.log
build_dir = Path(__file__).parents[1]
log_file = build_dir / "packets.log"
self._logger = logging.getLogger("build.packets")
self._logger.setLevel(logging.DEBUG)
self._logger.propagate = False
self._logger.handlers.clear()
handler = logging.FileHandler(log_file, mode="a", encoding="utf-8")
handler.setLevel(logging.DEBUG)
handler.setFormatter(logging.Formatter("%(message)s"))
self._logger.addHandler(handler)
def log(self, packet_type: str, payload: dict[str, Any] | None = None) -> None:
"""Log a packet as JSON.
Args:
packet_type: The type of packet
payload: The packet payload
"""
if not self._enabled or not self._logger:
return
try:
output = json.dumps(payload, indent=2, default=str) if payload else "{}"
self._logger.debug(f"\n=== {packet_type} ===\n{output}")
except Exception:
self._logger.debug(f"\n=== {packet_type} ===\n{payload}")
def log_raw(self, label: str, data: Any) -> None:
"""Log raw data with a label.
Args:
label: A label for this log entry
data: Any data to log
"""
if not self._enabled or not self._logger:
return
try:
if isinstance(data, (dict, list)):
output = json.dumps(data, indent=2, default=str)
else:
output = str(data)
self._logger.debug(f"\n=== {label} ===\n{output}")
except Exception:
self._logger.debug(f"\n=== {label} ===\n{data}")
# Singleton instance
_packet_logger: PacketLogger | None = None
def get_packet_logger() -> PacketLogger:
"""Get the singleton packet logger instance."""
global _packet_logger
if _packet_logger is None:
_packet_logger = PacketLogger()
return _packet_logger

View File

@@ -0,0 +1,68 @@
"""Build Mode packet types for streaming agent responses.
This module defines CUSTOM Onyx packet types that extend ACP (Agent Client Protocol).
ACP events are passed through directly from the agent - this module only contains
Onyx-specific extensions like artifacts and file operations.
All packets use SSE (Server-Sent Events) format with `event: message` and include
a `type` field to distinguish packet types.
ACP events (passed through directly from acp.schema):
- agent_message_chunk: Text/image content from agent
- agent_thought_chunk: Agent's internal reasoning
- tool_call_start: Tool invocation started
- tool_call_progress: Tool execution progress/result
- agent_plan_update: Agent's execution plan
- current_mode_update: Agent mode change
- prompt_response: Agent finished processing
- error: An error occurred
Custom Onyx packets (defined here):
- error: Onyx-specific errors (e.g., session not found)
Based on:
- Agent Client Protocol (ACP): https://agentclientprotocol.com
"""
from datetime import datetime
from datetime import timezone
from typing import Any
from typing import Literal
from pydantic import BaseModel
from pydantic import Field
# =============================================================================
# Base Packet Type
# =============================================================================
class BasePacket(BaseModel):
"""Base packet with common fields for all custom Onyx packet types."""
type: str
timestamp: str = Field(
default_factory=lambda: datetime.now(tz=timezone.utc).isoformat()
)
# =============================================================================
# Custom Onyx Packets
# =============================================================================
class ErrorPacket(BasePacket):
"""An Onyx-specific error occurred (e.g., session not found, sandbox not running)."""
type: Literal["error"] = "error"
message: str
code: int | None = None
details: dict[str, Any] | None = None
# =============================================================================
# Union Type for Custom Onyx Packets
# =============================================================================
BuildPacket = ErrorPacket

View File

@@ -0,0 +1,90 @@
"""Rate limiting logic for Build Mode."""
from datetime import datetime
from datetime import timedelta
from datetime import timezone
from typing import Literal
from sqlalchemy.orm import Session
from onyx.db.models import User
from onyx.server.features.build.api.models import RateLimitResponse
from onyx.server.features.build.api.subscription_check import is_user_subscribed
from onyx.server.features.build.db.rate_limit import count_user_messages_in_window
from onyx.server.features.build.db.rate_limit import count_user_messages_total
from onyx.server.features.build.db.rate_limit import get_oldest_message_timestamp
from shared_configs.configs import MULTI_TENANT
def get_user_rate_limit_status(
user: User,
db_session: Session,
) -> RateLimitResponse:
"""
Get the rate limit status for a user.
Rate limits:
- Cloud (MULTI_TENANT=true):
- Subscribed users: 50 messages per week (rolling 7-day window)
- Non-subscribed users: 5 messages (lifetime total)
- Self-hosted (MULTI_TENANT=false):
- Unlimited (no rate limiting)
Args:
user: The user object (None for unauthenticated users)
db_session: Database session
Returns:
RateLimitResponse with current limit status
"""
# Self-hosted deployments have no rate limits
if not MULTI_TENANT:
return RateLimitResponse(
is_limited=False,
limit_type="weekly",
messages_used=0,
limit=0, # 0 indicates unlimited
reset_timestamp=None,
)
# Determine subscription status
is_subscribed = is_user_subscribed(user, db_session)
# Set limits based on subscription
limit = 50 if is_subscribed else 5
limit_type: Literal["weekly", "total"] = "weekly" if is_subscribed else "total"
# Count messages
user_id = user.id if user else None
if user_id is None:
# Unauthenticated users have no usage
messages_used = 0
reset_timestamp = None
elif limit_type == "weekly":
# Subscribed: rolling 7-day window
cutoff_time = datetime.now(tz=timezone.utc) - timedelta(days=7)
messages_used = count_user_messages_in_window(user_id, cutoff_time, db_session)
# Calculate reset timestamp (when oldest message ages out)
# Only show reset time if user is at or over the limit
if messages_used >= limit:
oldest_msg = get_oldest_message_timestamp(user_id, cutoff_time, db_session)
if oldest_msg:
reset_time = oldest_msg + timedelta(days=7)
reset_timestamp = reset_time.isoformat()
else:
reset_timestamp = None
else:
reset_timestamp = None
else:
# Non-subscribed: lifetime total
messages_used = count_user_messages_total(user_id, db_session)
reset_timestamp = None
return RateLimitResponse(
is_limited=messages_used >= limit,
limit_type=limit_type,
messages_used=messages_used,
limit=limit,
reset_timestamp=reset_timestamp,
)

View File

@@ -0,0 +1,450 @@
"""API endpoints for Build Mode session management."""
from uuid import UUID
from fastapi import APIRouter
from fastapi import Depends
from fastapi import File
from fastapi import HTTPException
from fastapi import Response
from fastapi import UploadFile
from sqlalchemy.orm import Session
from onyx.auth.users import current_user
from onyx.db.engine.sql_engine import get_session
from onyx.db.models import User
from onyx.server.features.build.api.models import ArtifactResponse
from onyx.server.features.build.api.models import DirectoryListing
from onyx.server.features.build.api.models import SessionCreateRequest
from onyx.server.features.build.api.models import SessionListResponse
from onyx.server.features.build.api.models import SessionNameGenerateResponse
from onyx.server.features.build.api.models import SessionResponse
from onyx.server.features.build.api.models import SessionUpdateRequest
from onyx.server.features.build.api.models import UploadResponse
from onyx.server.features.build.api.models import WebappInfo
from onyx.server.features.build.db.sandbox import get_sandbox_by_user_id
from onyx.server.features.build.session.manager import SessionManager
from onyx.server.features.build.session.manager import UploadLimitExceededError
from onyx.server.features.build.utils import sanitize_filename
from onyx.server.features.build.utils import validate_file
from onyx.utils.logger import setup_logger
logger = setup_logger()
router = APIRouter(prefix="/sessions")
# =============================================================================
# Session Management Endpoints
# =============================================================================
@router.get("", response_model=SessionListResponse)
def list_sessions(
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> SessionListResponse:
"""List all build sessions for the current user."""
session_manager = SessionManager(db_session)
sessions = session_manager.list_sessions(user.id)
# Get the user's sandbox (shared across all sessions)
sandbox = get_sandbox_by_user_id(db_session, user.id)
return SessionListResponse(
sessions=[SessionResponse.from_model(session, sandbox) for session in sessions]
)
@router.post("", response_model=SessionResponse)
def create_session(
request: SessionCreateRequest,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> SessionResponse:
"""
Create or get an existing empty build session.
Creates a sandbox with the necessary file structure and returns a session ID.
Uses SessionManager for session and sandbox provisioning.
This endpoint is atomic - if sandbox provisioning fails, no database
records are created (transaction is rolled back).
"""
session_manager = SessionManager(db_session)
try:
# Only pass user_work_area and user_level if demo data is enabled
# This prevents org_info directory creation when demo data is disabled
build_session = session_manager.get_or_create_empty_session(
user.id,
user_work_area=(
request.user_work_area if request.demo_data_enabled else None
),
user_level=request.user_level if request.demo_data_enabled else None,
llm_provider_type=request.llm_provider_type,
llm_model_name=request.llm_model_name,
)
db_session.commit()
except ValueError as e:
# Max concurrent sandboxes reached or other validation error
logger.exception("Sandbox provisioning failed")
db_session.rollback()
raise HTTPException(status_code=429, detail=str(e))
except Exception as e:
# Sandbox provisioning failed - rollback to remove any uncommitted records
db_session.rollback()
logger.error(f"Sandbox provisioning failed: {e}")
raise HTTPException(
status_code=500,
detail=f"Sandbox provisioning failed: {e}",
)
# Get the user's sandbox to include in response
sandbox = get_sandbox_by_user_id(db_session, user.id)
return SessionResponse.from_model(build_session, sandbox)
@router.get("/{session_id}", response_model=SessionResponse)
def get_session_details(
session_id: UUID,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> SessionResponse:
"""
Get details of a specific build session.
If the sandbox is terminated, this will restore it synchronously.
"""
session_manager = SessionManager(db_session)
session = session_manager.get_session(session_id, user.id)
if session is None:
raise HTTPException(status_code=404, detail="Session not found")
# Get the user's sandbox to include in response
sandbox = get_sandbox_by_user_id(db_session, user.id)
return SessionResponse.from_model(session, sandbox)
@router.post("/{session_id}/generate-name", response_model=SessionNameGenerateResponse)
def generate_session_name(
session_id: UUID,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> SessionNameGenerateResponse:
"""Generate a session name using LLM based on the first user message."""
session_manager = SessionManager(db_session)
generated_name = session_manager.generate_session_name(session_id, user.id)
if generated_name is None:
raise HTTPException(status_code=404, detail="Session not found")
return SessionNameGenerateResponse(name=generated_name)
@router.put("/{session_id}/name", response_model=SessionResponse)
def update_session_name(
session_id: UUID,
request: SessionUpdateRequest,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> SessionResponse:
"""Update the name of a build session."""
session_manager = SessionManager(db_session)
session = session_manager.update_session_name(session_id, user.id, request.name)
if session is None:
raise HTTPException(status_code=404, detail="Session not found")
# Get the user's sandbox to include in response
sandbox = get_sandbox_by_user_id(db_session, user.id)
return SessionResponse.from_model(session, sandbox)
@router.delete("/{session_id}", response_model=None)
def delete_session(
session_id: UUID,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> Response:
"""Delete a build session and all associated data.
This endpoint is atomic - if sandbox termination fails, the session
is NOT deleted (transaction is rolled back).
"""
session_manager = SessionManager(db_session)
try:
success = session_manager.delete_session(session_id, user.id)
if not success:
raise HTTPException(status_code=404, detail="Session not found")
db_session.commit()
except HTTPException:
# Re-raise HTTP exceptions (like 404) without rollback
raise
except Exception as e:
# Sandbox termination failed - rollback to preserve session
db_session.rollback()
logger.error(f"Failed to delete session {session_id}: {e}")
raise HTTPException(
status_code=500,
detail=f"Failed to delete session: {e}",
)
return Response(status_code=204)
# =============================================================================
# Artifact Endpoints
# =============================================================================
@router.get(
"/{session_id}/artifacts",
response_model=list[ArtifactResponse],
)
def list_artifacts(
session_id: UUID,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> list[dict]:
"""List artifacts generated in the session."""
user_id: UUID = user.id
session_manager = SessionManager(db_session)
artifacts = session_manager.list_artifacts(session_id, user_id)
if artifacts is None:
raise HTTPException(status_code=404, detail="Session not found")
return artifacts
@router.get("/{session_id}/files", response_model=DirectoryListing)
def list_directory(
session_id: UUID,
path: str = "",
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> DirectoryListing:
"""
List files and directories in the sandbox.
Args:
session_id: The session ID
path: Relative path from sandbox root (empty string for root)
Returns:
DirectoryListing with sorted entries (directories first, then files)
"""
user_id: UUID = user.id
session_manager = SessionManager(db_session)
try:
listing = session_manager.list_directory(session_id, user_id, path)
except ValueError as e:
error_message = str(e)
if "path traversal" in error_message.lower():
raise HTTPException(status_code=403, detail="Access denied")
elif "not found" in error_message.lower():
raise HTTPException(status_code=404, detail="Directory not found")
elif "not a directory" in error_message.lower():
raise HTTPException(status_code=400, detail="Path is not a directory")
raise HTTPException(status_code=400, detail=error_message)
if listing is None:
raise HTTPException(status_code=404, detail="Session not found")
return listing
@router.get("/{session_id}/artifacts/{path:path}")
def download_artifact(
session_id: UUID,
path: str,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> Response:
"""Download a specific artifact file."""
user_id: UUID = user.id
session_manager = SessionManager(db_session)
try:
result = session_manager.download_artifact(session_id, user_id, path)
except ValueError as e:
error_message = str(e)
if (
"path traversal" in error_message.lower()
or "access denied" in error_message.lower()
):
raise HTTPException(status_code=403, detail="Access denied")
elif "directory" in error_message.lower():
raise HTTPException(status_code=400, detail="Cannot download directory")
raise HTTPException(status_code=400, detail=error_message)
if result is None:
raise HTTPException(status_code=404, detail="Artifact not found")
content, mime_type, filename = result
# Handle Unicode filenames in Content-Disposition header
# HTTP headers require Latin-1 encoding, so we use RFC 5987 for Unicode
try:
# Try Latin-1 encoding first (ASCII-compatible filenames)
filename.encode("latin-1")
content_disposition = f'attachment; filename="{filename}"'
except UnicodeEncodeError:
# Use RFC 5987 encoding for Unicode filenames
from urllib.parse import quote
encoded_filename = quote(filename, safe="")
content_disposition = f"attachment; filename*=UTF-8''{encoded_filename}"
return Response(
content=content,
media_type=mime_type,
headers={
"Content-Disposition": content_disposition,
},
)
@router.get("/{session_id}/webapp", response_model=WebappInfo)
def get_webapp_info(
session_id: UUID,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> WebappInfo:
"""
Get webapp information for a session.
Returns whether a webapp exists, its URL, and the sandbox status.
"""
user_id: UUID = user.id
session_manager = SessionManager(db_session)
webapp_info = session_manager.get_webapp_info(session_id, user_id)
if webapp_info is None:
raise HTTPException(status_code=404, detail="Session not found")
return WebappInfo(**webapp_info)
@router.get("/{session_id}/webapp/download")
def download_webapp(
session_id: UUID,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> Response:
"""
Download the webapp directory as a zip file.
Returns the entire outputs/web directory as a zip archive.
"""
user_id: UUID = user.id
session_manager = SessionManager(db_session)
result = session_manager.download_webapp_zip(session_id, user_id)
if result is None:
raise HTTPException(status_code=404, detail="Webapp not found")
zip_bytes, filename = result
return Response(
content=zip_bytes,
media_type="application/zip",
headers={
"Content-Disposition": f'attachment; filename="{filename}"',
},
)
@router.post("/{session_id}/upload", response_model=UploadResponse)
async def upload_file_endpoint(
session_id: UUID,
file: UploadFile = File(...),
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> UploadResponse:
"""Upload a file to the session's sandbox.
The file will be placed in the sandbox's attachments directory.
"""
user_id: UUID = user.id
session_manager = SessionManager(db_session)
if not file.filename:
raise HTTPException(status_code=400, detail="File has no filename")
# Read file content
content = await file.read()
# Validate file (extension, mime type, size)
is_valid, error = validate_file(file.filename, file.content_type, len(content))
if not is_valid:
raise HTTPException(status_code=400, detail=error)
# Sanitize filename
safe_filename = sanitize_filename(file.filename)
try:
relative_path, _ = session_manager.upload_file(
session_id=session_id,
user_id=user_id,
filename=safe_filename,
content=content,
)
except UploadLimitExceededError as e:
# Return 429 for limit exceeded errors
raise HTTPException(status_code=429, detail=str(e))
except ValueError as e:
error_message = str(e)
if "not found" in error_message.lower():
raise HTTPException(status_code=404, detail=error_message)
raise HTTPException(status_code=400, detail=error_message)
return UploadResponse(
filename=safe_filename,
path=relative_path,
size_bytes=len(content),
)
@router.delete("/{session_id}/files/{path:path}", response_model=None)
def delete_file_endpoint(
session_id: UUID,
path: str,
user: User = Depends(current_user),
db_session: Session = Depends(get_session),
) -> Response:
"""Delete a file from the session's sandbox.
Args:
session_id: The session ID
path: Relative path to the file (e.g., "attachments/doc.pdf")
"""
user_id: UUID = user.id
session_manager = SessionManager(db_session)
try:
deleted = session_manager.delete_file(session_id, user_id, path)
except ValueError as e:
error_message = str(e)
if "path traversal" in error_message.lower():
raise HTTPException(status_code=403, detail="Access denied")
elif "not found" in error_message.lower():
raise HTTPException(status_code=404, detail=error_message)
elif "directory" in error_message.lower():
raise HTTPException(status_code=400, detail="Cannot delete directory")
raise HTTPException(status_code=400, detail=error_message)
if not deleted:
raise HTTPException(status_code=404, detail="File not found")
return Response(status_code=204)

View File

@@ -0,0 +1,52 @@
"""Subscription detection for Build Mode rate limiting."""
from sqlalchemy.orm import Session
from onyx.configs.app_configs import DEV_MODE
from onyx.db.models import User
from onyx.server.usage_limits import is_tenant_on_trial_fn
from onyx.utils.logger import setup_logger
from shared_configs.configs import MULTI_TENANT
from shared_configs.contextvars import get_current_tenant_id
logger = setup_logger()
def is_user_subscribed(user: User, db_session: Session) -> bool:
"""
Check if a user has an active subscription.
For cloud (MULTI_TENANT=true):
- Checks Stripe billing via control plane
- Returns True if tenant is NOT on trial (subscribed = NOT on trial)
For self-hosted (MULTI_TENANT=false):
- Checks license metadata
- Returns True if license status is ACTIVE
Args:
user: The user object (None for unauthenticated users)
db_session: Database session
Returns:
True if user has active subscription, False otherwise
"""
if DEV_MODE:
return True
if user is None:
return False
if MULTI_TENANT:
# Cloud: check Stripe billing via control plane
tenant_id = get_current_tenant_id()
try:
on_trial = is_tenant_on_trial_fn(tenant_id)
# Subscribed = NOT on trial
return not on_trial
except Exception as e:
logger.warning(f"Subscription check failed for tenant {tenant_id}: {e}")
# Default to non-subscribed (safer/more restrictive)
return False
return True

View File

@@ -0,0 +1,115 @@
import os
from enum import Enum
from pathlib import Path
class SandboxBackend(str, Enum):
"""Backend mode for sandbox operations.
LOCAL: Development mode - no snapshots, no automatic cleanup
KUBERNETES: Production mode - full snapshots and cleanup
"""
LOCAL = "local"
KUBERNETES = "kubernetes"
# Sandbox backend mode (controls snapshot and cleanup behavior)
# "local" = no snapshots, no cleanup (for development)
# "kubernetes" = full snapshots and cleanup (for production)
SANDBOX_BACKEND = SandboxBackend(os.environ.get("SANDBOX_BACKEND", "local"))
# Persistent Document Storage Configuration
# When enabled, indexed documents are written to local filesystem with hierarchical structure
PERSISTENT_DOCUMENT_STORAGE_ENABLED = (
os.environ.get("PERSISTENT_DOCUMENT_STORAGE_ENABLED", "").lower() == "true"
)
# Base directory path for persistent document storage (local filesystem)
# Example: /var/onyx/indexed-docs or /app/indexed-docs
PERSISTENT_DOCUMENT_STORAGE_PATH = os.environ.get(
"PERSISTENT_DOCUMENT_STORAGE_PATH", ""
)
# Demo Data Path
# Local: Source tree path (relative to this file)
# Kubernetes: Baked into container image at /workspace/demo-data
_THIS_FILE = Path(__file__)
DEMO_DATA_PATH = str(
_THIS_FILE.parent / "sandbox" / "kubernetes" / "docker" / "demo_data"
)
# Sandbox filesystem paths
SANDBOX_BASE_PATH = os.environ.get("SANDBOX_BASE_PATH", "/tmp/onyx-sandboxes")
OUTPUTS_TEMPLATE_PATH = os.environ.get("OUTPUTS_TEMPLATE_PATH", "/templates/outputs")
VENV_TEMPLATE_PATH = os.environ.get("VENV_TEMPLATE_PATH", "/templates/venv")
# Sandbox agent configuration
SANDBOX_AGENT_COMMAND = os.environ.get("SANDBOX_AGENT_COMMAND", "opencode").split()
# OpenCode disabled tools (comma-separated list)
# Available tools: bash, edit, write, read, grep, glob, list, lsp, patch,
# skill, todowrite, todoread, webfetch, question
# Example: "question,webfetch" to disable user questions and web fetching
_disabled_tools_str = os.environ.get("OPENCODE_DISABLED_TOOLS", "question")
OPENCODE_DISABLED_TOOLS: list[str] = [
t.strip() for t in _disabled_tools_str.split(",") if t.strip()
]
# Sandbox lifecycle configuration
SANDBOX_IDLE_TIMEOUT_SECONDS = int(
os.environ.get("SANDBOX_IDLE_TIMEOUT_SECONDS", "900")
)
SANDBOX_MAX_CONCURRENT_PER_ORG = int(
os.environ.get("SANDBOX_MAX_CONCURRENT_PER_ORG", "10")
)
# Sandbox snapshot storage
SANDBOX_SNAPSHOTS_BUCKET = os.environ.get(
"SANDBOX_SNAPSHOTS_BUCKET", "sandbox-snapshots"
)
# Next.js preview server port range
SANDBOX_NEXTJS_PORT_START = int(os.environ.get("SANDBOX_NEXTJS_PORT_START", "3010"))
SANDBOX_NEXTJS_PORT_END = int(os.environ.get("SANDBOX_NEXTJS_PORT_END", "3100"))
# File upload configuration
MAX_UPLOAD_FILE_SIZE_MB = int(os.environ.get("BUILD_MAX_UPLOAD_FILE_SIZE_MB", "50"))
MAX_UPLOAD_FILE_SIZE_BYTES = MAX_UPLOAD_FILE_SIZE_MB * 1024 * 1024
MAX_UPLOAD_FILES_PER_SESSION = int(
os.environ.get("BUILD_MAX_UPLOAD_FILES_PER_SESSION", "20")
)
MAX_TOTAL_UPLOAD_SIZE_MB = int(os.environ.get("BUILD_MAX_TOTAL_UPLOAD_SIZE_MB", "200"))
MAX_TOTAL_UPLOAD_SIZE_BYTES = MAX_TOTAL_UPLOAD_SIZE_MB * 1024 * 1024
ATTACHMENTS_DIRECTORY = "attachments"
# ============================================================================
# Kubernetes Sandbox Configuration
# Only used when SANDBOX_BACKEND = "kubernetes"
# ============================================================================
# Namespace where sandbox pods are created
SANDBOX_NAMESPACE = os.environ.get("SANDBOX_NAMESPACE", "onyx-sandboxes")
# Container image for sandbox pods
# Should include Next.js template and opencode CLI
SANDBOX_CONTAINER_IMAGE = os.environ.get(
"SANDBOX_CONTAINER_IMAGE", "onyxdotapp/sandbox:latest"
)
# S3 bucket for sandbox file storage (snapshots, knowledge files, uploads)
# Path structure: s3://{bucket}/{tenant_id}/snapshots/{session_id}/{snapshot_id}.tar.gz
# s3://{bucket}/{tenant_id}/knowledge/{user_id}/
# s3://{bucket}/{tenant_id}/uploads/{session_id}/
SANDBOX_S3_BUCKET = os.environ.get("SANDBOX_S3_BUCKET", "onyx-sandbox-files")
# Service account for sandbox pods (NO IRSA - no AWS API access)
SANDBOX_SERVICE_ACCOUNT_NAME = os.environ.get(
"SANDBOX_SERVICE_ACCOUNT_NAME", "sandbox-runner"
)
# Service account for init container (has IRSA for S3 access)
SANDBOX_FILE_SYNC_SERVICE_ACCOUNT = os.environ.get(
"SANDBOX_FILE_SYNC_SERVICE_ACCOUNT", "sandbox-file-sync"
)

View File

@@ -0,0 +1 @@
# Database operations for the build feature

View File

@@ -0,0 +1,519 @@
"""Database operations for Build Mode sessions."""
from datetime import datetime
from datetime import timedelta
from typing import Any
from uuid import UUID
from sqlalchemy import desc
from sqlalchemy import exists
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from sqlalchemy.orm import Session
from onyx.configs.constants import MessageType
from onyx.db.enums import BuildSessionStatus
from onyx.db.enums import SandboxStatus
from onyx.db.models import Artifact
from onyx.db.models import BuildMessage
from onyx.db.models import BuildSession
from onyx.db.models import LLMProvider as LLMProviderModel
from onyx.db.models import Sandbox
from onyx.db.models import Snapshot
from onyx.server.features.build.configs import SANDBOX_NEXTJS_PORT_END
from onyx.server.features.build.configs import SANDBOX_NEXTJS_PORT_START
from onyx.server.manage.llm.models import LLMProviderView
from onyx.utils.logger import setup_logger
logger = setup_logger()
def create_build_session__no_commit(
user_id: UUID,
db_session: Session,
name: str | None = None,
) -> BuildSession:
"""Create a new build session for the given user.
NOTE: This function uses flush() instead of commit(). The caller is
responsible for committing the transaction when ready.
"""
session = BuildSession(
user_id=user_id,
name=name,
status=BuildSessionStatus.ACTIVE,
)
db_session.add(session)
db_session.flush()
logger.info(f"Created build session {session.id} for user {user_id}")
return session
def get_build_session(
session_id: UUID,
user_id: UUID,
db_session: Session,
) -> BuildSession | None:
"""Get a build session by ID, ensuring it belongs to the user."""
return (
db_session.query(BuildSession)
.filter(
BuildSession.id == session_id,
BuildSession.user_id == user_id,
)
.one_or_none()
)
def get_user_build_sessions(
user_id: UUID,
db_session: Session,
limit: int = 100,
) -> list[BuildSession]:
"""Get all build sessions for a user that have at least 1 message.
Excludes empty (pre-provisioned) sessions from the listing.
"""
return (
db_session.query(BuildSession)
.join(BuildMessage) # Inner join excludes empty sessions
.filter(BuildSession.user_id == user_id)
.group_by(BuildSession.id)
.order_by(desc(BuildSession.created_at))
.limit(limit)
.all()
)
def get_empty_session_for_user(
user_id: UUID,
db_session: Session,
max_age_minutes: int = 30,
) -> BuildSession | None:
"""Get the user's empty session (0 messages) if one exists and is recent."""
cutoff = datetime.utcnow() - timedelta(minutes=max_age_minutes)
return (
db_session.query(BuildSession)
.filter(
BuildSession.user_id == user_id,
BuildSession.created_at > cutoff,
~exists().where(BuildMessage.session_id == BuildSession.id),
)
.first()
)
def update_session_activity(
session_id: UUID,
db_session: Session,
) -> None:
"""Update the last activity timestamp for a session."""
session = (
db_session.query(BuildSession)
.filter(BuildSession.id == session_id)
.one_or_none()
)
if session:
session.last_activity_at = datetime.utcnow()
db_session.commit()
def update_session_status(
session_id: UUID,
status: BuildSessionStatus,
db_session: Session,
) -> None:
"""Update the status of a build session."""
session = (
db_session.query(BuildSession)
.filter(BuildSession.id == session_id)
.one_or_none()
)
if session:
session.status = status
db_session.commit()
logger.info(f"Updated build session {session_id} status to {status}")
def delete_build_session__no_commit(
session_id: UUID,
user_id: UUID,
db_session: Session,
) -> bool:
"""Delete a build session and all related data.
NOTE: This function uses flush() instead of commit(). The caller is
responsible for committing the transaction when ready.
"""
session = get_build_session(session_id, user_id, db_session)
if not session:
return False
db_session.delete(session)
db_session.flush()
logger.info(f"Deleted build session {session_id}")
return True
# Sandbox operations
# NOTE: Most sandbox operations have moved to sandbox.py
# These remain here for convenience in session-related workflows
def update_sandbox_status(
sandbox_id: UUID,
status: SandboxStatus,
db_session: Session,
container_id: str | None = None,
) -> None:
"""Update the status of a sandbox."""
sandbox = db_session.query(Sandbox).filter(Sandbox.id == sandbox_id).one_or_none()
if sandbox:
sandbox.status = status
if container_id is not None:
sandbox.container_id = container_id
sandbox.last_heartbeat = datetime.utcnow()
db_session.commit()
logger.info(f"Updated sandbox {sandbox_id} status to {status}")
def update_sandbox_heartbeat(
sandbox_id: UUID,
db_session: Session,
) -> None:
"""Update the heartbeat timestamp for a sandbox."""
sandbox = db_session.query(Sandbox).filter(Sandbox.id == sandbox_id).one_or_none()
if sandbox:
sandbox.last_heartbeat = datetime.utcnow()
db_session.commit()
# Artifact operations
def create_artifact(
session_id: UUID,
artifact_type: str,
path: str,
name: str,
db_session: Session,
) -> Artifact:
"""Create a new artifact record."""
artifact = Artifact(
session_id=session_id,
type=artifact_type,
path=path,
name=name,
)
db_session.add(artifact)
db_session.commit()
db_session.refresh(artifact)
logger.info(f"Created artifact {artifact.id} for session {session_id}")
return artifact
def get_session_artifacts(
session_id: UUID,
db_session: Session,
) -> list[Artifact]:
"""Get all artifacts for a session."""
return (
db_session.query(Artifact)
.filter(Artifact.session_id == session_id)
.order_by(desc(Artifact.created_at))
.all()
)
def update_artifact(
artifact_id: UUID,
db_session: Session,
path: str | None = None,
name: str | None = None,
) -> None:
"""Update artifact metadata."""
artifact = (
db_session.query(Artifact).filter(Artifact.id == artifact_id).one_or_none()
)
if artifact:
if path is not None:
artifact.path = path
if name is not None:
artifact.name = name
artifact.updated_at = datetime.utcnow()
db_session.commit()
logger.info(f"Updated artifact {artifact_id}")
# Snapshot operations
def create_snapshot(
session_id: UUID,
storage_path: str,
size_bytes: int,
db_session: Session,
) -> Snapshot:
"""Create a new snapshot record."""
snapshot = Snapshot(
session_id=session_id,
storage_path=storage_path,
size_bytes=size_bytes,
)
db_session.add(snapshot)
db_session.commit()
db_session.refresh(snapshot)
logger.info(f"Created snapshot {snapshot.id} for session {session_id}")
return snapshot
# Message operations
def create_message(
session_id: UUID,
message_type: MessageType,
turn_index: int,
message_metadata: dict[str, Any],
db_session: Session,
) -> BuildMessage:
"""Create a new message in a build session.
All message data is stored in message_metadata as JSON.
Args:
session_id: Session UUID
message_type: Type of message (USER, ASSISTANT, SYSTEM)
turn_index: 0-indexed user message number this message belongs to
message_metadata: Required structured data (the raw ACP packet JSON)
db_session: Database session
"""
message = BuildMessage(
session_id=session_id,
turn_index=turn_index,
type=message_type,
message_metadata=message_metadata,
)
db_session.add(message)
db_session.commit()
db_session.refresh(message)
logger.info(
f"Created {message_type.value} message {message.id} for session {session_id} "
f"turn={turn_index} type={message_metadata.get('type')}"
)
return message
def update_message(
message_id: UUID,
message_metadata: dict[str, Any],
db_session: Session,
) -> BuildMessage | None:
"""Update an existing message's metadata.
Used for upserting agent_plan_update messages.
Args:
message_id: The message UUID to update
message_metadata: New metadata to set
db_session: Database session
Returns:
Updated BuildMessage or None if not found
"""
message = (
db_session.query(BuildMessage).filter(BuildMessage.id == message_id).first()
)
if message is None:
return None
message.message_metadata = message_metadata
db_session.commit()
db_session.refresh(message)
logger.info(
f"Updated message {message_id} metadata type={message_metadata.get('type')}"
)
return message
def upsert_agent_plan(
session_id: UUID,
turn_index: int,
plan_metadata: dict[str, Any],
db_session: Session,
existing_plan_id: UUID | None = None,
) -> BuildMessage:
"""Upsert an agent plan - update if exists, create if not.
Each session/turn should only have one agent_plan_update message.
This function updates the existing plan message or creates a new one.
Args:
session_id: Session UUID
turn_index: Current turn index
plan_metadata: The agent_plan_update packet data
db_session: Database session
existing_plan_id: ID of existing plan message to update (if known)
Returns:
The created or updated BuildMessage
"""
if existing_plan_id:
# Fast path: we know the plan ID
updated = update_message(existing_plan_id, plan_metadata, db_session)
if updated:
return updated
# Check if a plan already exists for this session/turn
existing_plan = (
db_session.query(BuildMessage)
.filter(
BuildMessage.session_id == session_id,
BuildMessage.turn_index == turn_index,
BuildMessage.message_metadata["type"].astext == "agent_plan_update",
)
.first()
)
if existing_plan:
existing_plan.message_metadata = plan_metadata
db_session.commit()
db_session.refresh(existing_plan)
logger.info(
f"Updated agent_plan_update message {existing_plan.id} for session {session_id}"
)
return existing_plan
# Create new plan message
return create_message(
session_id=session_id,
message_type=MessageType.ASSISTANT,
turn_index=turn_index,
message_metadata=plan_metadata,
db_session=db_session,
)
def get_session_messages(
session_id: UUID,
db_session: Session,
) -> list[BuildMessage]:
"""Get all messages for a session, ordered by turn index and creation time."""
return (
db_session.query(BuildMessage)
.filter(BuildMessage.session_id == session_id)
.order_by(BuildMessage.turn_index, BuildMessage.created_at)
.all()
)
def _is_port_available(port: int) -> bool:
"""Check if a port is available by attempting to bind to it.
Checks both IPv4 and IPv6 wildcard addresses to properly detect
if anything is listening on the port, regardless of address family.
"""
import socket
logger.debug(f"Checking if port {port} is available")
# Check IPv4 wildcard (0.0.0.0) - this will detect any IPv4 listener
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("0.0.0.0", port))
logger.debug(f"Port {port} IPv4 wildcard bind successful")
except OSError as e:
logger.debug(f"Port {port} IPv4 wildcard not available: {e}")
return False
# Check IPv6 wildcard (::) - this will detect any IPv6 listener
try:
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# IPV6_V6ONLY must be False to allow dual-stack behavior
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock.bind(("::", port))
logger.debug(f"Port {port} IPv6 wildcard bind successful")
except OSError as e:
logger.debug(f"Port {port} IPv6 wildcard not available: {e}")
return False
logger.debug(f"Port {port} is available")
return True
def allocate_nextjs_port(db_session: Session) -> int:
"""Allocate an available port for a new session.
Finds the first available port in the configured range by checking
both database allocations and system-level port availability.
Args:
db_session: Database session for querying allocated ports
Returns:
An available port number
Raises:
RuntimeError: If no ports are available in the configured range
"""
from onyx.db.models import BuildSession
# Get all currently allocated ports from active sessions
allocated_ports = set(
db_session.query(BuildSession.nextjs_port)
.filter(BuildSession.nextjs_port.isnot(None))
.all()
)
allocated_ports = {port[0] for port in allocated_ports if port[0] is not None}
# Find first port that's not in DB and not currently bound
for port in range(SANDBOX_NEXTJS_PORT_START, SANDBOX_NEXTJS_PORT_END):
if port not in allocated_ports and _is_port_available(port):
return port
raise RuntimeError(
f"No available ports in range [{SANDBOX_NEXTJS_PORT_START}, {SANDBOX_NEXTJS_PORT_END})"
)
def fetch_llm_provider_by_type_for_build_mode(
db_session: Session, provider_type: str
) -> LLMProviderView | None:
"""Fetch an LLM provider by its provider type (e.g., "anthropic", "openai").
Resolution priority:
1. First try to find a provider named "build-mode-{type}" (e.g., "build-mode-anthropic")
2. If not found, fall back to any provider that matches the type
Args:
db_session: Database session
provider_type: The provider type (e.g., "anthropic", "openai", "openrouter")
Returns:
LLMProviderView if found, None otherwise
"""
from onyx.db.llm import fetch_existing_llm_provider
# First try to find a "build-mode-{type}" provider
build_mode_name = f"build-mode-{provider_type}"
provider_model = fetch_existing_llm_provider(
name=build_mode_name, db_session=db_session
)
# If not found, fall back to any provider that matches the type
if not provider_model:
provider_model = db_session.scalar(
select(LLMProviderModel)
.where(LLMProviderModel.provider == provider_type)
.options(
selectinload(LLMProviderModel.model_configurations),
selectinload(LLMProviderModel.groups),
selectinload(LLMProviderModel.personas),
)
)
if not provider_model:
return None
return LLMProviderView.from_model(provider_model)

View File

@@ -0,0 +1,96 @@
"""Database queries for Build Mode rate limiting."""
from datetime import datetime
from uuid import UUID
from sqlalchemy import func
from sqlalchemy.orm import Session
from onyx.configs.constants import MessageType
from onyx.db.models import BuildMessage
from onyx.db.models import BuildSession
def count_user_messages_in_window(
user_id: UUID,
cutoff_time: datetime,
db_session: Session,
) -> int:
"""
Count USER messages for a user since cutoff_time.
Args:
user_id: The user's UUID
cutoff_time: Only count messages created at or after this time
db_session: Database session
Returns:
Number of USER messages in the time window
"""
return (
db_session.query(func.count(BuildMessage.id))
.join(BuildSession, BuildMessage.session_id == BuildSession.id)
.filter(
BuildSession.user_id == user_id,
BuildMessage.type == MessageType.USER,
BuildMessage.created_at >= cutoff_time,
)
.scalar()
or 0
)
def count_user_messages_total(user_id: UUID, db_session: Session) -> int:
"""
Count all USER messages for a user (lifetime total).
Args:
user_id: The user's UUID
db_session: Database session
Returns:
Total number of USER messages
"""
return (
db_session.query(func.count(BuildMessage.id))
.join(BuildSession, BuildMessage.session_id == BuildSession.id)
.filter(
BuildSession.user_id == user_id,
BuildMessage.type == MessageType.USER,
)
.scalar()
or 0
)
def get_oldest_message_timestamp(
user_id: UUID,
cutoff_time: datetime,
db_session: Session,
) -> datetime | None:
"""
Get the timestamp of the oldest USER message in the time window.
Used to calculate when the rate limit will reset (when the oldest
message ages out of the rolling window).
Args:
user_id: The user's UUID
cutoff_time: Only consider messages created at or after this time
db_session: Database session
Returns:
Timestamp of oldest message in window, or None if no messages
"""
return (
db_session.query(BuildMessage.created_at)
.join(BuildSession, BuildMessage.session_id == BuildSession.id)
.filter(
BuildSession.user_id == user_id,
BuildMessage.type == MessageType.USER,
BuildMessage.created_at >= cutoff_time,
)
.order_by(BuildMessage.created_at.asc())
.limit(1)
.scalar()
)

View File

@@ -0,0 +1,206 @@
"""Database operations for CLI agent sandbox management."""
import datetime
from uuid import UUID
from sqlalchemy import func
from sqlalchemy import select
from sqlalchemy.orm import Session
from onyx.db.enums import SandboxStatus
from onyx.db.models import Sandbox
from onyx.db.models import Snapshot
from onyx.utils.logger import setup_logger
logger = setup_logger()
def create_sandbox__no_commit(
db_session: Session,
user_id: UUID,
) -> Sandbox:
"""Create a new sandbox record for a user.
NOTE: This function uses flush() instead of commit(). The caller is
responsible for committing the transaction when ready.
"""
sandbox = Sandbox(
user_id=user_id,
status=SandboxStatus.PROVISIONING,
)
db_session.add(sandbox)
db_session.flush()
return sandbox
def get_sandbox_by_user_id(db_session: Session, user_id: UUID) -> Sandbox | None:
"""Get sandbox by user ID (primary lookup method)."""
stmt = select(Sandbox).where(Sandbox.user_id == user_id)
return db_session.execute(stmt).scalar_one_or_none()
def get_sandbox_by_session_id(db_session: Session, session_id: UUID) -> Sandbox | None:
"""Get sandbox by session ID (compatibility function).
This function provides backwards compatibility during the transition to
user-owned sandboxes. It looks up the session's user_id, then finds the
user's sandbox.
NOTE: This will be removed in a future phase when all callers are updated
to use get_sandbox_by_user_id() directly.
"""
from onyx.db.models import BuildSession
stmt = select(BuildSession.user_id).where(BuildSession.id == session_id)
result = db_session.execute(stmt).scalar_one_or_none()
if result is None:
return None
return get_sandbox_by_user_id(db_session, result)
def get_sandbox_by_id(db_session: Session, sandbox_id: UUID) -> Sandbox | None:
"""Get sandbox by its ID."""
stmt = select(Sandbox).where(Sandbox.id == sandbox_id)
return db_session.execute(stmt).scalar_one_or_none()
def update_sandbox_status__no_commit(
db_session: Session,
sandbox_id: UUID,
status: SandboxStatus,
) -> Sandbox:
"""Update sandbox status.
NOTE: This function uses flush() instead of commit(). The caller is
responsible for committing the transaction when ready.
"""
sandbox = get_sandbox_by_id(db_session, sandbox_id)
if not sandbox:
raise ValueError(f"Sandbox {sandbox_id} not found")
sandbox.status = status
db_session.flush()
return sandbox
def update_sandbox_heartbeat(db_session: Session, sandbox_id: UUID) -> Sandbox:
"""Update sandbox last_heartbeat to now."""
sandbox = get_sandbox_by_id(db_session, sandbox_id)
if not sandbox:
raise ValueError(f"Sandbox {sandbox_id} not found")
sandbox.last_heartbeat = datetime.datetime.now(datetime.timezone.utc)
db_session.commit()
return sandbox
def get_idle_sandboxes(
db_session: Session, idle_threshold_seconds: int
) -> list[Sandbox]:
"""Get sandboxes that have been idle longer than threshold."""
threshold_time = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(
seconds=idle_threshold_seconds
)
stmt = select(Sandbox).where(
Sandbox.status.in_([SandboxStatus.RUNNING, SandboxStatus.IDLE]),
Sandbox.last_heartbeat < threshold_time,
)
return list(db_session.execute(stmt).scalars().all())
def get_running_sandbox_count_by_tenant(db_session: Session, tenant_id: str) -> int:
"""Get count of running sandboxes for a tenant (for limit enforcement).
Note: tenant_id parameter is kept for API compatibility but is not used
since Sandbox model no longer has tenant_id. This function returns
the count of all running sandboxes.
"""
stmt = select(func.count(Sandbox.id)).where(
Sandbox.status.in_([SandboxStatus.RUNNING, SandboxStatus.IDLE])
)
result = db_session.execute(stmt).scalar()
return result or 0
def create_snapshot(
db_session: Session,
session_id: UUID,
storage_path: str,
size_bytes: int,
) -> Snapshot:
"""Create a snapshot record for a session."""
snapshot = Snapshot(
session_id=session_id,
storage_path=storage_path,
size_bytes=size_bytes,
)
db_session.add(snapshot)
db_session.commit()
return snapshot
def get_latest_snapshot_for_session(
db_session: Session, session_id: UUID
) -> Snapshot | None:
"""Get most recent snapshot for a session."""
stmt = (
select(Snapshot)
.where(Snapshot.session_id == session_id)
.order_by(Snapshot.created_at.desc())
.limit(1)
)
return db_session.execute(stmt).scalar_one_or_none()
def get_snapshots_for_session(db_session: Session, session_id: UUID) -> list[Snapshot]:
"""Get all snapshots for a session, ordered by creation time descending."""
stmt = (
select(Snapshot)
.where(Snapshot.session_id == session_id)
.order_by(Snapshot.created_at.desc())
)
return list(db_session.execute(stmt).scalars().all())
def delete_old_snapshots(
db_session: Session, tenant_id: str, retention_days: int
) -> int:
"""Delete snapshots older than retention period, return count deleted.
Note: tenant_id parameter is kept for API compatibility but is not used
since Snapshot model no longer has tenant_id. This function deletes
all snapshots older than the retention period.
"""
cutoff_time = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(
days=retention_days
)
stmt = select(Snapshot).where(
Snapshot.created_at < cutoff_time,
)
old_snapshots = db_session.execute(stmt).scalars().all()
count = 0
for snapshot in old_snapshots:
db_session.delete(snapshot)
count += 1
if count > 0:
db_session.commit()
return count
def delete_snapshot(db_session: Session, snapshot_id: UUID) -> bool:
"""Delete a specific snapshot by ID. Returns True if deleted, False if not found."""
stmt = select(Snapshot).where(Snapshot.id == snapshot_id)
snapshot = db_session.execute(stmt).scalar_one_or_none()
if not snapshot:
return False
db_session.delete(snapshot)
db_session.commit()
return True

View File

@@ -0,0 +1,352 @@
# Onyx Sandbox System
This directory contains the implementation of Onyx's sandbox system for running OpenCode agents in isolated environments.
## Overview
The sandbox system provides isolated execution environments where OpenCode agents can build web applications, run code, and interact with knowledge files. Each sandbox includes:
- **Next.js development environment** - Lightweight Next.js scaffold with shadcn/ui and Recharts for building UIs
- **Python virtual environment** - Pre-installed packages for data processing
- **OpenCode agent** - AI coding agent with access to tools and MCP servers
- **Knowledge files** - Access to indexed documents and user uploads
## Architecture
### Deployment Modes
1. **Local Mode** (`SANDBOX_BACKEND=local`)
- Sandboxes run as directories on the local filesystem
- No automatic cleanup or snapshots
- Suitable for development and testing
2. **Kubernetes Mode** (`SANDBOX_BACKEND=kubernetes`)
- Sandboxes run as Kubernetes pods
- Automatic snapshots to S3
- Auto-cleanup of idle sandboxes
- Production-ready with resource isolation
### Directory Structure
```
/workspace/ # Sandbox root (in container)
├── outputs/ # Working directory
│ ├── web/ # Lightweight Next.js app (shadcn/ui, Recharts)
│ ├── slides/ # Generated presentations
│ ├── markdown/ # Generated documents
│ └── graphs/ # Generated visualizations
├── .venv/ # Python virtual environment
├── files/ # Symlink to knowledge files
├── attachments/ # User uploads
├── AGENTS.md # Agent instructions
└── .opencode/
└── skills/ # Agent skills
```
## Setup
### Running via Docker/Kubernetes (Zero Setup!) 🎉
**No setup required!** Just build and deploy:
```bash
# Build backend image (includes both templates)
cd backend
docker build -f Dockerfile.sandbox-templates -t onyxdotapp/backend:latest .
# Build sandbox container (lightweight runner)
cd onyx/server/features/build/sandbox/kubernetes/docker
docker build -t onyxdotapp/sandbox:latest .
# Deploy with docker-compose or kubectl - sandboxes work immediately!
```
**How it works:**
- **Backend image**: Contains both templates at build time:
- Web template at `/templates/outputs/web` (lightweight Next.js scaffold, ~2MB)
- Python venv template at `/templates/venv` (pre-installed packages, ~50MB)
- **Init container** (Kubernetes only): Syncs knowledge files from S3
- **Sandbox startup**: Runs `npm install` (for fresh dependency locks) + `next dev`
### Running Backend Directly (Without Docker)
**Only needed if you're running the Onyx backend outside of Docker.** Most developers use Docker and can skip this section.
If you're running the backend Python process directly on your machine, you need templates at `/templates/`:
#### Web Template
The web template is a lightweight Next.js app (Next.js 16, React 19, shadcn/ui, Recharts) checked into the codebase at `backend/onyx/server/features/build/templates/outputs/web/`.
For local development, create a symlink to this template:
```bash
sudo mkdir -p /templates/outputs
sudo ln -s $(pwd)/backend/onyx/server/features/build/templates/outputs/web /templates/outputs/web
```
#### Python Venv Template
If you don't have a venv template, create it:
```bash
# Use the utility script
cd backend
python -m onyx.server.features.build.sandbox.util.build_venv_template
# Or manually
python3 -m venv /templates/venv
/templates/venv/bin/pip install -r backend/onyx/server/features/build/sandbox/kubernetes/docker/initial-requirements.txt
```
**That's it!** When sandboxes are created:
1. Web template is copied from `/templates/outputs/web`
2. Python venv is copied from `/templates/venv`
3. `npm install` runs automatically to install fresh Next.js dependencies
## OpenCode Configuration
Each sandbox includes an OpenCode agent configured with:
- **LLM Provider**: Anthropic, OpenAI, Google, Bedrock, or Azure
- **Extended thinking**: High reasoning effort / thinking budgets for complex tasks
- **Tool permissions**: File operations, bash commands, web access
- **Disabled tools**: Configurable via `OPENCODE_DISABLED_TOOLS` env var
Configuration is generated dynamically in `templates/opencode_config.py`.
## Key Components
### Managers
- **`base.py`** - Abstract base class defining the sandbox interface
- **`local/manager.py`** - Filesystem-based sandbox manager for local development
- **`kubernetes/manager.py`** - Kubernetes-based sandbox manager for production
### Managers (Shared)
- **`manager/directory_manager.py`** - Creates sandbox directory structure and copies templates
- **`manager/snapshot_manager.py`** - Handles snapshot creation and restoration
### Utilities
- **`util/opencode_config.py`** - Generates OpenCode configuration with MCP support
- **`util/agent_instructions.py`** - Generates agent instructions (AGENTS.md)
- **`util/build_venv_template.py`** - Utility to build Python venv template for local development
### Templates
- **`../templates/outputs/web/`** - Lightweight Next.js scaffold (shadcn/ui, Recharts) versioned with the backend code
### Kubernetes Specific
- **`kubernetes/docker/Dockerfile`** - Sandbox container image (runs Next.js + OpenCode)
- **`kubernetes/docker/entrypoint.sh`** - Container startup script
## Environment Variables
### Core Settings
```bash
# Sandbox backend mode
SANDBOX_BACKEND=local|kubernetes # Default: local
# Template paths (local mode)
OUTPUTS_TEMPLATE_PATH=/templates/outputs # Default: /templates/outputs
VENV_TEMPLATE_PATH=/templates/venv # Default: /templates/venv
# Sandbox base path (local mode)
SANDBOX_BASE_PATH=/tmp/onyx-sandboxes # Default: /tmp/onyx-sandboxes
# OpenCode configuration
OPENCODE_DISABLED_TOOLS=question # Comma-separated list, default: question
```
### Kubernetes Settings
```bash
# Kubernetes namespace
SANDBOX_NAMESPACE=onyx-sandboxes # Default: onyx-sandboxes
# Container image
SANDBOX_CONTAINER_IMAGE=onyxdotapp/sandbox:latest
# S3 bucket for snapshots and files
SANDBOX_S3_BUCKET=onyx-sandbox-files # Default: onyx-sandbox-files
# Service accounts
SANDBOX_SERVICE_ACCOUNT_NAME=sandbox-runner # No AWS access
SANDBOX_FILE_SYNC_SERVICE_ACCOUNT=sandbox-file-sync # Has S3 access via IRSA
```
### Lifecycle Settings
```bash
# Idle timeout before cleanup (seconds)
SANDBOX_IDLE_TIMEOUT_SECONDS=900 # Default: 900 (15 minutes)
# Max concurrent sandboxes per organization
SANDBOX_MAX_CONCURRENT_PER_ORG=10 # Default: 10
# Next.js port range (local mode)
SANDBOX_NEXTJS_PORT_START=3010 # Default: 3010
SANDBOX_NEXTJS_PORT_END=3100 # Default: 3100
```
## Testing
### Integration Tests
```bash
# Test local sandbox provisioning
uv run pytest backend/tests/integration/sandbox/test_local_sandbox.py
# Test Kubernetes sandbox provisioning (requires k8s cluster)
uv run pytest backend/tests/integration/sandbox/test_kubernetes_sandbox.py
```
### Manual Testing
```bash
# Start a local sandbox session
curl -X POST http://localhost:3000/api/build/session \
-H "Content-Type: application/json" \
-d '{
"user_id": "user-123",
"file_system_path": "/path/to/files"
}'
# Send a message to the agent
curl -X POST http://localhost:3000/api/build/session/{session_id}/message \
-H "Content-Type: application/json" \
-d '{
"message": "Create a simple web page"
}'
```
## Troubleshooting
### Sandbox Stuck in PROVISIONING (Kubernetes)
**Symptoms**: Sandbox status never changes from `PROVISIONING`
**Solutions**:
- Check pod logs: `kubectl logs -n onyx-sandboxes sandbox-{sandbox-id}`
- Check init container: `kubectl logs -n onyx-sandboxes sandbox-{sandbox-id} -c file-sync`
- Verify init container completed: `kubectl describe pod -n onyx-sandboxes sandbox-{sandbox-id}`
- Check S3 bucket access: Ensure init container service account has IRSA configured
### Next.js Server Won't Start
**Symptoms**: Sandbox provisioned but web preview doesn't load
**Solutions**:
- **Local mode**: Check if port is already in use
- **Docker/K8s**: Check container logs: `kubectl logs -n onyx-sandboxes sandbox-{sandbox-id}`
- Verify npm install succeeded (check entrypoint.sh logs)
- Check that web template was copied: `kubectl exec -n onyx-sandboxes sandbox-{sandbox-id} -- ls /workspace/outputs/web`
### Templates Not Found (Local Mode)
**Symptoms**: `RuntimeError: Sandbox templates are missing`
**Solution**: Set up templates as described in the "Local Development" section above:
```bash
# Symlink web template
sudo ln -s $(pwd)/backend/onyx/server/features/build/templates/outputs/web /templates/outputs/web
# Create Python venv
python3 -m venv /templates/venv
/templates/venv/bin/pip install -r backend/onyx/server/features/build/sandbox/kubernetes/docker/initial-requirements.txt
```
### Permission Denied
**Symptoms**: `Permission denied` error accessing `/templates/`
**Solution**: Either use sudo when creating symlinks, or use custom paths:
```bash
export OUTPUTS_TEMPLATE_PATH=$HOME/.onyx/templates/outputs
export VENV_TEMPLATE_PATH=$HOME/.onyx/templates/venv
# Then symlink to your home directory
mkdir -p $HOME/.onyx/templates/outputs
ln -s $(pwd)/backend/onyx/server/features/build/templates/outputs/web $HOME/.onyx/templates/outputs/web
```
## Security Considerations
### Sandbox Isolation
- **Kubernetes pods** run with restricted security context (non-root, no privilege escalation)
- **Init containers** have S3 access for file sync, but main sandbox container does NOT
- **Network policies** can restrict sandbox egress traffic
- **Resource limits** prevent resource exhaustion
### Credentials Management
- LLM API keys are passed as environment variables (not stored in sandbox)
- User file access is read-only via symlinks
- Snapshots are isolated per tenant in S3
## Development
### Adding New MCP Servers
1. Add MCP configuration to `templates/opencode_config.py`:
```python
config["mcp"] = {
"my-mcp": {
"type": "local",
"command": ["npx", "@my/mcp@latest"],
"enabled": True,
}
}
```
2. Install required npm packages in web template (if needed)
3. Rebuild Docker image and templates
### Modifying Agent Instructions
Edit `AGENTS.template.md` in the build directory. This is populated with dynamic content by `templates/agent_instructions.py`.
### Adding New Tools/Permissions
Update `templates/opencode_config.py` to add/remove tool permissions in the `permission` section.
## Template Details
### Web Template
The lightweight Next.js template (`backend/onyx/server/features/build/templates/outputs/web/`) includes:
- **Framework**: Next.js 16.1.4 with React 19.2.3
- **UI Library**: shadcn/ui components with Radix UI primitives
- **Styling**: Tailwind CSS v4 with custom theming support
- **Charts**: Recharts for data visualization
- **Size**: ~2MB (excluding node_modules, which are installed fresh per sandbox)
This template provides a modern development environment without the complexity of the full Onyx application, allowing agents to build custom UIs quickly.
### Python Venv Template
The Python venv (`/templates/venv/`) includes packages from `initial-requirements.txt`:
- Data processing: pandas, numpy, polars
- HTTP clients: requests, httpx
- Utilities: python-dotenv, pydantic
## References
- [OpenCode Documentation](https://docs.opencode.ai)
- [Next.js Documentation](https://nextjs.org/docs)
- [shadcn/ui Components](https://ui.shadcn.com)

View File

@@ -0,0 +1,44 @@
"""
Sandbox module for CLI agent filesystem-based isolation.
This module provides lightweight sandbox management for CLI-based AI agent sessions.
Each sandbox is a directory on the local filesystem or a Kubernetes pod.
Usage:
from onyx.server.features.build.sandbox import get_sandbox_manager
# Get the appropriate sandbox manager based on SANDBOX_BACKEND config
sandbox_manager = get_sandbox_manager()
# Use the sandbox manager
sandbox_info = sandbox_manager.provision(...)
Module structure:
- base.py: SandboxManager ABC and get_sandbox_manager() factory
- models.py: Shared Pydantic models
- local/: Local filesystem-based implementation for development
- kubernetes/: Kubernetes pod-based implementation for production
- internal/: Shared internal utilities (snapshot manager)
"""
from onyx.server.features.build.sandbox.base import get_sandbox_manager
from onyx.server.features.build.sandbox.base import SandboxManager
from onyx.server.features.build.sandbox.local.local_sandbox_manager import (
LocalSandboxManager,
)
from onyx.server.features.build.sandbox.models import FilesystemEntry
from onyx.server.features.build.sandbox.models import SandboxInfo
from onyx.server.features.build.sandbox.models import SnapshotInfo
__all__ = [
# Factory function (preferred)
"get_sandbox_manager",
# Interface
"SandboxManager",
# Implementations
"LocalSandboxManager",
# Models
"SandboxInfo",
"SnapshotInfo",
"FilesystemEntry",
]

View File

@@ -0,0 +1,378 @@
"""Abstract base class and factory for sandbox operations.
SandboxManager is the abstract interface for sandbox lifecycle management.
Use get_sandbox_manager() to get the appropriate implementation based on SANDBOX_BACKEND.
IMPORTANT: SandboxManager implementations must NOT interface with the database directly.
All database operations should be handled by the caller (SessionManager, Celery tasks, etc.).
Architecture Note (User-Shared Sandbox Model):
- One sandbox (container/pod) is shared across all of a user's sessions
- provision() creates the user's sandbox with shared files/ directory
- setup_session_workspace() creates per-session workspace within the sandbox
- cleanup_session_workspace() removes session workspace on session delete
- terminate() destroys the entire sandbox (all sessions)
"""
import threading
from abc import ABC
from abc import abstractmethod
from collections.abc import Generator
from typing import Any
from uuid import UUID
from onyx.server.features.build.configs import SANDBOX_BACKEND
from onyx.server.features.build.configs import SandboxBackend
from onyx.server.features.build.sandbox.models import FilesystemEntry
from onyx.server.features.build.sandbox.models import LLMProviderConfig
from onyx.server.features.build.sandbox.models import SandboxInfo
from onyx.server.features.build.sandbox.models import SnapshotResult
from onyx.utils.logger import setup_logger
logger = setup_logger()
# ACPEvent is a union type defined in both local and kubernetes modules
# Using Any here to avoid circular imports - the actual type checking
# happens in the implementation modules
ACPEvent = Any
class SandboxManager(ABC):
"""Abstract interface for sandbox operations.
Defines the contract for sandbox lifecycle management including:
- Provisioning and termination (user-level)
- Session workspace setup and cleanup (session-level)
- Snapshot creation (session-level)
- Health checks
- Agent communication (session-level)
- Filesystem operations (session-level)
Directory Structure:
$SANDBOX_ROOT/
├── files/ # SHARED - symlink to user's persistent documents
└── sessions/
├── $session_id_1/ # Per-session workspace
│ ├── outputs/ # Agent output for this session
│ │ └── web/ # Next.js app
│ ├── venv/ # Python virtual environment
│ ├── skills/ # Opencode skills
│ ├── AGENTS.md # Agent instructions
│ ├── opencode.json # LLM config
│ └── attachments/
└── $session_id_2/
└── ...
IMPORTANT: Implementations must NOT interface with the database directly.
All database operations should be handled by the caller.
Use get_sandbox_manager() to get the appropriate implementation.
"""
@abstractmethod
def provision(
self,
sandbox_id: UUID,
user_id: UUID,
tenant_id: str,
llm_config: LLMProviderConfig,
) -> SandboxInfo:
"""Provision a new sandbox for a user.
Creates the sandbox container/directory with:
- sessions/ directory for per-session workspaces
NOTE: This does NOT set up session-specific workspaces.
Call setup_session_workspace() after provisioning to create a session workspace.
Args:
sandbox_id: Unique identifier for the sandbox
user_id: User identifier who owns this sandbox
tenant_id: Tenant identifier for multi-tenant isolation
llm_config: LLM provider configuration (for default config)
Returns:
SandboxInfo with the provisioned sandbox details
Raises:
RuntimeError: If provisioning fails
"""
...
@abstractmethod
def terminate(self, sandbox_id: UUID) -> None:
"""Terminate a sandbox and clean up all resources.
Destroys the entire sandbox including all session workspaces.
Use cleanup_session_workspace() to remove individual sessions.
Args:
sandbox_id: The sandbox ID to terminate
"""
...
@abstractmethod
def setup_session_workspace(
self,
sandbox_id: UUID,
session_id: UUID,
llm_config: LLMProviderConfig,
nextjs_port: int,
file_system_path: str | None = None,
snapshot_path: str | None = None,
user_name: str | None = None,
user_role: str | None = None,
user_work_area: str | None = None,
user_level: str | None = None,
use_demo_data: bool = False,
) -> None:
"""Set up a session workspace within an existing sandbox.
Creates the per-session directory structure:
- sessions/$session_id/outputs/ (from snapshot or template)
- sessions/$session_id/venv/
- sessions/$session_id/skills/
- sessions/$session_id/files/ (symlink to demo data or user files)
- sessions/$session_id/AGENTS.md
- sessions/$session_id/opencode.json
- sessions/$session_id/attachments/
- sessions/$session_id/org_info/ (if demo data enabled)
Args:
sandbox_id: The sandbox ID (must be provisioned)
session_id: The session ID for this workspace
llm_config: LLM provider configuration for opencode.json
file_system_path: Path to user's knowledge/source files
snapshot_path: Optional storage path to restore outputs from
user_name: User's name for personalization in AGENTS.md
user_role: User's role/title for personalization in AGENTS.md
user_work_area: User's work area for demo persona (e.g., "engineering")
user_level: User's level for demo persona (e.g., "ic", "manager")
use_demo_data: If True, symlink files/ to demo data; else to user files
Raises:
RuntimeError: If workspace setup fails
"""
...
@abstractmethod
def cleanup_session_workspace(
self,
sandbox_id: UUID,
session_id: UUID,
) -> None:
"""Clean up a session workspace (on session delete).
Removes the session directory: sessions/$session_id/
Does NOT terminate the sandbox - other sessions may still be using it.
Args:
sandbox_id: The sandbox ID
session_id: The session ID to clean up
"""
...
@abstractmethod
def create_snapshot(
self,
sandbox_id: UUID,
session_id: UUID,
tenant_id: str,
) -> SnapshotResult | None:
"""Create a snapshot of a session's outputs directory.
Captures only the session-specific outputs:
sessions/$session_id/outputs/
Does NOT include: venv, skills, AGENTS.md, opencode.json, attachments
Does NOT include: shared files/ directory
Args:
sandbox_id: The sandbox ID
session_id: The session ID to snapshot
tenant_id: Tenant identifier for storage path
Returns:
SnapshotResult with storage path and size, or None if
snapshots are disabled for this backend
Raises:
RuntimeError: If snapshot creation fails
"""
...
@abstractmethod
def health_check(
self, sandbox_id: UUID, nextjs_port: int | None, timeout: float = 60.0
) -> bool:
"""Check if the sandbox is healthy.
Args:
sandbox_id: The sandbox ID to check
nextjs_port: The Next.js port (for local backend health checks)
Returns:
True if sandbox is healthy, False otherwise
"""
...
@abstractmethod
def send_message(
self,
sandbox_id: UUID,
session_id: UUID,
message: str,
) -> Generator[ACPEvent, None, None]:
"""Send a message to the CLI agent and stream typed ACP events.
The agent runs in the session-specific workspace:
sessions/$session_id/
Args:
sandbox_id: The sandbox ID
session_id: The session ID (determines workspace directory)
message: The message content to send
Yields:
Typed ACP schema event objects
Raises:
RuntimeError: If agent communication fails
"""
...
@abstractmethod
def list_directory(
self, sandbox_id: UUID, session_id: UUID, path: str
) -> list[FilesystemEntry]:
"""List contents of a directory in the session's outputs directory.
Args:
sandbox_id: The sandbox ID
session_id: The session ID
path: Relative path within sessions/$session_id/outputs/
Returns:
List of FilesystemEntry objects sorted by directory first, then name
Raises:
ValueError: If path traversal attempted or path is not a directory
"""
...
@abstractmethod
def read_file(self, sandbox_id: UUID, session_id: UUID, path: str) -> bytes:
"""Read a file from the session's outputs directory.
Args:
sandbox_id: The sandbox ID
session_id: The session ID
path: Relative path within sessions/$session_id/outputs/
Returns:
File contents as bytes
Raises:
ValueError: If path traversal attempted or path is not a file
"""
...
@abstractmethod
def upload_file(
self,
sandbox_id: UUID,
session_id: UUID,
filename: str,
content: bytes,
) -> str:
"""Upload a file to the session's attachments directory.
Args:
sandbox_id: The sandbox ID
session_id: The session ID
filename: Sanitized filename
content: File content as bytes
Returns:
Relative path where file was saved (e.g., "attachments/doc.pdf")
Raises:
RuntimeError: If upload fails
"""
...
@abstractmethod
def delete_file(
self,
sandbox_id: UUID,
session_id: UUID,
path: str,
) -> bool:
"""Delete a file from the session's workspace.
Args:
sandbox_id: The sandbox ID
session_id: The session ID
path: Relative path to the file (e.g., "attachments/doc.pdf")
Returns:
True if file was deleted, False if not found
Raises:
ValueError: If path traversal attempted
"""
...
@abstractmethod
def get_upload_stats(
self,
sandbox_id: UUID,
session_id: UUID,
) -> tuple[int, int]:
"""Get current file count and total size for a session's attachments.
Args:
sandbox_id: The sandbox ID
session_id: The session ID
Returns:
Tuple of (file_count, total_size_bytes)
"""
...
# Singleton instance cache for the factory
_sandbox_manager_instance: SandboxManager | None = None
_sandbox_manager_lock = threading.Lock()
def get_sandbox_manager() -> SandboxManager:
"""Get the appropriate SandboxManager implementation based on SANDBOX_BACKEND.
Returns:
SandboxManager instance:
- LocalSandboxManager for local backend (development)
- KubernetesSandboxManager for kubernetes backend (production)
"""
global _sandbox_manager_instance
if _sandbox_manager_instance is None:
with _sandbox_manager_lock:
if _sandbox_manager_instance is None:
if SANDBOX_BACKEND == SandboxBackend.LOCAL:
from onyx.server.features.build.sandbox.local.local_sandbox_manager import (
LocalSandboxManager,
)
_sandbox_manager_instance = LocalSandboxManager()
elif SANDBOX_BACKEND == SandboxBackend.KUBERNETES:
from onyx.server.features.build.sandbox.kubernetes.kubernetes_sandbox_manager import (
KubernetesSandboxManager,
)
_sandbox_manager_instance = KubernetesSandboxManager()
logger.info("Using KubernetesSandboxManager for sandbox operations")
else:
raise ValueError(f"Unknown sandbox backend: {SANDBOX_BACKEND}")
return _sandbox_manager_instance

View File

@@ -0,0 +1,16 @@
"""Kubernetes-based sandbox implementation.
This module provides the KubernetesSandboxManager for production deployments
that run sandboxes as isolated Kubernetes pods.
Internal implementation details (acp_http_client) are in the internal/
subdirectory and should not be used directly.
"""
from onyx.server.features.build.sandbox.kubernetes.kubernetes_sandbox_manager import (
KubernetesSandboxManager,
)
__all__ = [
"KubernetesSandboxManager",
]

View File

@@ -0,0 +1,97 @@
# Sandbox Container Image
#
# User-shared sandbox model:
# - One pod per user, shared across all user's sessions
# - Session workspaces created via kubectl exec (setup_session_workspace)
# - OpenCode agent runs via kubectl exec when needed
#
# Directory structure (created by init container + session setup):
# /workspace/
# ├── demo-data/ # Demo data (baked into image, for demo sessions)
# ├── files/ # User's knowledge files (synced from S3)
# ├── templates/ # Output templates (baked into image)
# └── sessions/ # Per-session workspaces (created via exec)
# └── $session_id/
# ├── files/ # Symlink to /workspace/demo-data or /workspace/files
# ├── outputs/
# ├── AGENTS.md
# └── opencode.json
FROM node:20-slim
# Install system dependencies
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
python3-venv \
curl \
git \
procps \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user (matches pod securityContext)
# Handle existing user/group with UID/GID 1000 in base image
RUN EXISTING_USER=$(id -nu 1000 2>/dev/null || echo ""); \
EXISTING_GROUP=$(getent group 1000 | cut -d: -f1 2>/dev/null || echo ""); \
if [ -n "$EXISTING_GROUP" ] && [ "$EXISTING_GROUP" != "sandbox" ]; then \
groupmod -n sandbox $EXISTING_GROUP; \
elif [ -z "$EXISTING_GROUP" ]; then \
groupadd -g 1000 sandbox; \
fi; \
if [ -n "$EXISTING_USER" ] && [ "$EXISTING_USER" != "sandbox" ]; then \
usermod -l sandbox -g sandbox $EXISTING_USER; \
usermod -d /home/sandbox -m sandbox; \
usermod -s /bin/bash sandbox; \
elif [ -z "$EXISTING_USER" ]; then \
useradd -u 1000 -g sandbox -m -s /bin/bash sandbox; \
fi
# Create workspace directories
RUN mkdir -p workspace/sessions /workspace/files /workspace/templates /workspace/demo-data && \
chown -R sandbox:sandbox /workspace
# Copy outputs template (web app scaffold, without node_modules)
COPY --exclude=.next --exclude=node_modules templates/outputs /workspace/templates/outputs
RUN chown -R sandbox:sandbox /workspace/templates
# Copy demo data (optional - will be empty if demo_data/ doesn't exist)
COPY demo_data /workspace/demo-data
RUN chown -R sandbox:sandbox /workspace/demo-data
# Copy and install Python requirements into a venv
COPY initial-requirements.txt /tmp/initial-requirements.txt
RUN python3 -m venv /workspace/.venv && \
/workspace/.venv/bin/pip install --upgrade pip && \
/workspace/.venv/bin/pip install -r /tmp/initial-requirements.txt && \
rm /tmp/initial-requirements.txt && \
chown -R sandbox:sandbox /workspace/.venv
# Add venv to PATH so python/pip use it by default
ENV PATH="/workspace/.venv/bin:${PATH}"
# Install opencode CLI as sandbox user so it goes to their home directory
USER sandbox
RUN curl -fsSL https://opencode.ai/install | bash
USER root
# Add opencode to PATH (installs to ~/.opencode/bin)
ENV PATH="/home/sandbox/.opencode/bin:${PATH}"
# Set ownership
RUN chown -R sandbox:sandbox /workspace
# Copy scripts
COPY generate_agents_md.py /usr/local/bin/generate_agents_md.py
RUN chmod +x /usr/local/bin/generate_agents_md.py
# Switch to non-root user
USER sandbox
WORKDIR /workspace
# Expose ports
# - 3000: Next.js dev server (started per-session if needed)
# - 8081: OpenCode ACP HTTP server (started via exec)
EXPOSE 3000 8081
# Keep container alive - all work done via kubectl exec
CMD ["sleep", "infinity"]

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_1669418",
"semantic_identifier": "1669418 - Vividcedar Marketing - sales_pitch - Discussion with Ricardo Alvarez",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-02-21 00:00:00",
"metadata": {
"meeting_date": "2025-02-21 00:00:00",
"duration_min": "18"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-02"
],
"year_month": "2025-02",
"meeting_title": "1669418 - Vividcedar Marketing - sales_pitch - Discussion with Ricardo Alvarez",
"organizer_email": "mina_park@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Mina Park: Hi Ricardo\u2014Mina Park here. Thanks for taking a few minutes. I\u2019ll keep it brief: I work with teams who are trying to get really crisp on which customer segments are actually worth targeting, and why.",
"link": "https://www.onyx.app/8189379-1"
},
{
"text": "Ricardo Alvarez: Hey Mina, sure\u2014what are you calling about?",
"link": "https://www.onyx.app/8189379-2"
},
{
"text": "Mina Park: I\u2019m reaching out because we built a product called Customer_Analyzer. It pulls together the signals you already have\u2014CRM fields, onboarding surveys, product usage, support tickets, even interview notes\u2014and turns that into a simple, defensible \u201cwho should we go after next\u201d answer. I noticed Vividcedar Marketing has been expanding campaigns across a few verticals, and I\u2019m guessing prioritization is getting harder.",
"link": "https://www.onyx.app/8189379-3"
},
{
"text": "Ricardo Alvarez: Yeah, that\u2019s fair. We\u2019re testing a couple industries and it\u2019s not always clear what\u2019s best, beyond lead volume.",
"link": "https://www.onyx.app/8189379-4"
},
{
"text": "Mina Park: Exactly. Lead volume is usually the loudest signal, but not the most profitable one. The core output we focus on is a fit score\u20140 to 100\u2014at the account and segment level. Then we show the driver breakdown, so it\u2019s not a black box: \u201cHere\u2019s why mid-market healthcare scores higher than SMB retail,\u201d for example, based on retention, support burden, time-to-value, and adoption patterns.",
"link": "https://www.onyx.app/8189379-5"
},
{
"text": "Ricardo Alvarez: Interesting. How do you get those inputs? We have pieces of that, but it\u2019s spread out.",
"link": "https://www.onyx.app/8189379-6"
},
{
"text": "Mina Park: That\u2019s the normal starting point. Customer_Analyzer ingests from your CRM and support system first\u2014because those are fast wins\u2014then you can layer in product usage events if you have them, and any structured survey data. The key is you define the fit drivers you care about\u2014industry, size, problem severity, support burden, churn risk\u2014and we compute the score per segment, plus a segment comparison view.",
"link": "https://www.onyx.app/8189379-7"
},
{
"text": "Ricardo Alvarez: When you say \u201csupport burden,\u201d what do you mean? Ticket volume?",
"link": "https://www.onyx.app/8189379-8"
},
{
"text": "Mina Park: Ticket volume is one piece, but we usually normalize it. Like: tickets per active account, time-to-resolution, escalations, and the categories of issues. Some segments generate a lot of \u201chow do I use this\u201d tickets early and then stabilize; others keep generating complex issues. Customer_Analyzer uses those trends as signals when it estimates fit.",
"link": "https://www.onyx.app/8189379-9"
},
{
"text": "Ricardo Alvarez: Okay. Our challenge is that some industries look good up front but end up taking tons of support later, and it kills margins.",
"link": "https://www.onyx.app/8189379-10"
},
{
"text": "Mina Park: That\u2019s a perfect use case. One common workflow is: \u201cPrioritize which industries to target next quarter using retention and support burden signals.\u201d So instead of deciding based on pipeline alone, you can answer: which industries convert well *and* renew well *and* don\u2019t overload support. The output is a recommended ICP profile based on observed outcomes\u2014retention, expansion, conversion.",
"link": "https://www.onyx.app/8189379-11"
},
{
"text": "Ricardo Alvarez: We do quarterly planning now and it\u2019s a lot of debate. People bring anecdotes, not evidence.",
"link": "https://www.onyx.app/8189379-12"
},
{
"text": "Mina Park: Right\u2014and that\u2019s exactly where the driver breakdown helps. If the score says \u201c72,\u201d we show the \u201cwhy\u201d: strong adoption patterns, low support burden, fast time-to-value, and low churn risk signals. Or the opposite: good conversion, but poor retention and high support cost. That way the conversation changes from opinions to trade-offs.",
"link": "https://www.onyx.app/8189379-13"
},
{
"text": "Ricardo Alvarez: How long does it take to get to something usable? I\u2019m not looking for a six-month analytics project.",
"link": "https://www.onyx.app/8189379-14"
},
{
"text": "Mina Park: Totally. We aim for a quick first pass\u2014usually a couple weeks to connect data sources and define fit drivers, then you have initial segment scoring. After that, teams iterate: add another driver, refine definitions, or split segments differently. But you\u2019re not waiting months to see value.",
"link": "https://www.onyx.app/8189379-15"
},
{
"text": "Ricardo Alvarez: And how do the segments get defined\u2014do we define them or does the system?",
"link": "https://www.onyx.app/8189379-16"
},
{
"text": "Mina Park: You can do either. Most teams start with how they already think: industry, company size, maybe geography or a key use case. Customer_Analyzer will compare those segments side-by-side. Then, based on outcomes, it can recommend a tighter ICP definition\u2014for example, \u201cmid-market manufacturing with high onboarding survey severity and fast activation,\u201d if that\u2019s what correlates with retention and expansion.",
"link": "https://www.onyx.app/8189379-17"
},
{
"text": "Ricardo Alvarez: That\u2019s helpful. We have CRM data pretty clean, but product usage is\u2026 not perfect.",
"link": "https://www.onyx.app/8189379-18"
},
{
"text": "Mina Park: That\u2019s okay. The fit estimation isn\u2019t dependent on perfect event tracking. If you have any proxy for adoption\u2014logins, activation milestones, feature usage\u2014it helps, but you can get a lot from CRM + support + retention outcomes alone. We also show confidence by driver, so you know what\u2019s solid versus what\u2019s inferred.",
"link": "https://www.onyx.app/8189379-19"
},
{
"text": "Ricardo Alvarez: What would you need from us to sanity-check whether this would work?",
"link": "https://www.onyx.app/8189379-20"
},
{
"text": "Mina Park: For a quick evaluation, I\u2019d ask for: (1) your current segmentation\u2014what industries you\u2019re debating for next quarter, (2) the retention or churn outcomes by account for the last 12\u201318 months, and (3) support ticket volume and resolution time by account. With that, we can run a lightweight fit estimation model and show you a segment comparison: where you\u2019re getting the best retention with the lowest support burden.",
"link": "https://www.onyx.app/8189379-21"
},
{
"text": "Ricardo Alvarez: And that\u2019s something you can show on a call?",
"link": "https://www.onyx.app/8189379-22"
},
{
"text": "Mina Park: Yes. The next step I\u2019d suggest is a 30\u201345 minute working session. You can bring whoever owns planning\u2014maybe someone from ops or product marketing\u2014and we\u2019ll map your fit drivers, then I\u2019ll show you what the output looks like: the fit score per segment, the breakdown, and what an ICP recommendation would look like for Vividcedar.",
"link": "https://www.onyx.app/8189379-23"
},
{
"text": "Ricardo Alvarez: I like the idea. My only concern is change management\u2014our team is used to deciding based on pipeline and gut feel.",
"link": "https://www.onyx.app/8189379-24"
},
{
"text": "Mina Park: That\u2019s common. The way we position it internally is: we\u2019re not replacing pipeline thinking, we\u2019re adding \u201cquality of revenue\u201d signals\u2014retention and support burden\u2014so your next-quarter bet doesn\u2019t create next-year churn. And because the model is explainable, it\u2019s easier to get buy-in. People can disagree with a driver, adjust it, and see the impact.",
"link": "https://www.onyx.app/8189379-25"
},
{
"text": "Ricardo Alvarez: Makes sense. Do you integrate with HubSpot? That\u2019s our CRM.",
"link": "https://www.onyx.app/8189379-26"
},
{
"text": "Mina Park: Yes\u2014HubSpot is straightforward for the CRM portion. For support, we can connect to the major ticketing tools, and if you have anything custom, we can start with exports. The goal is to get you a segment-level view quickly, not boil the ocean.",
"link": "https://www.onyx.app/8189379-27"
},
{
"text": "Ricardo Alvarez: Alright. If we do the working session, what would \u201csuccess\u201d look like by the end of it?",
"link": "https://www.onyx.app/8189379-28"
},
{
"text": "Mina Park: By the end, you\u2019d have: your fit drivers defined, your top segments scored with an initial fit score, and a clear view of what\u2019s driving those scores\u2014especially retention and support burden. Ideally you walk away with a short list like: \u201cTarget these two industries next quarter, deprioritize this one, and here\u2019s the evidence.\u201d",
"link": "https://www.onyx.app/8189379-29"
},
{
"text": "Ricardo Alvarez: That would be really useful for planning. Can we do next week?",
"link": "https://www.onyx.app/8189379-30"
},
{
"text": "Mina Park: Absolutely. I have Tuesday at 11am or Thursday at 2pm\u2014either work?",
"link": "https://www.onyx.app/8189379-31"
},
{
"text": "Ricardo Alvarez: Thursday at 2pm works. Send an invite.",
"link": "https://www.onyx.app/8189379-32"
},
{
"text": "Mina Park: Perfect. I\u2019ll send the calendar invite for Thursday at 2pm, and I\u2019ll include a short list of what to bring\u2014HubSpot segment fields, churn/retention outcomes, and support ticket metrics. Looking forward to it, Ricardo.",
"link": "https://www.onyx.app/8189379-33"
},
{
"text": "Ricardo Alvarez: Sounds good. Thanks Mina\u2014talk then.",
"link": "https://www.onyx.app/8189379-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "mina_park@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "ricardo_alvarez@vividcedar_marketing.onyx.app"
}
]
}

View File

@@ -0,0 +1,129 @@
{
"id": "FIREFLIES_4471164",
"semantic_identifier": "4471164 - Prairielark Education - sales_pitch - Discussion with David Green",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-02-28 00:00:00",
"metadata": {
"meeting_date": "2025-02-28 00:00:00",
"duration_min": "30"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-02"
],
"year_month": "2025-02",
"meeting_title": "4471164 - Prairielark Education - sales_pitch - Discussion with David Green",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi David\u2014thanks for taking a few minutes. I\u2019m Megan Foster. I work with teams that are getting swamped by customer issues coming in from Zendesk/Intercom, then getting re-triaged in Jira or Linear, and somehow the same \u201cproblem\u201d shows up in ten different places with no single source of truth. I wanted to share how Issue_Tracker helps centralize that and\u2014specifically\u2014how it helps Sales avoid late-stage deal slippage when a known issue is blocking a prospect.",
"link": "https://www.onyx.app/3770080-1"
},
{
"text": "David Green: Hi Megan, yeah, that\u2019s a familiar pain. We definitely have repeats of the same issue across tickets, and it\u2019s hard to know what\u2019s actually widespread versus just noisy. What exactly does Issue_Tracker do\u2014does it replace Zendesk or Jira?",
"link": "https://www.onyx.app/3770080-2"
},
{
"text": "Megan Foster: Great question\u2014it doesn\u2019t replace either. Think of Issue_Tracker as an intelligence and triage layer between support channels like Zendesk/Intercom and delivery tools like Jira/Linear. It continuously ingests customer-reported problems\u2014tickets, chats, email threads, even call-note summaries\u2014then it deduplicates and clusters them into \u201cissue objects\u201d that represent the underlying theme/root cause. Those issue objects stay linked back to the original conversations and forward to the engineering work, so you\u2019re not losing context or double-counting.",
"link": "https://www.onyx.app/3770080-3"
},
{
"text": "David Green: Okay, so it\u2019s like a middle layer. We\u2019ve tried tagging and macros in Zendesk, and it kind of works until it doesn\u2019t. How does the clustering actually help day to day?",
"link": "https://www.onyx.app/3770080-4"
},
{
"text": "Megan Foster: The feature most teams feel immediately is **issue_clustering**. Instead of relying on humans to apply consistent tags, Issue_Tracker automatically groups raw inputs into deduped clusters using semantic similarity plus rules you can configure\u2014like product area, platform, severity, or certain keywords you care about. Each cluster gets naming suggestions, representative examples, top phrases, and there\u2019s a simple merge/split workflow with guardrails so your team can correct it without creating chaos.",
"link": "https://www.onyx.app/3770080-5"
},
{
"text": "David Green: Interesting. We\u2019d need it to be accurate. Our support volume isn\u2019t massive, but it\u2019s steady, and the same integration issues pop up across districts. Does it keep track of what customers are affected?",
"link": "https://www.onyx.app/3770080-6"
},
{
"text": "Megan Foster: Exactly. Each cluster maintains impacted products/areas and a suspected root-cause category\u2014bug vs UX vs missing feature vs integration, that kind of thing\u2014and it keeps links back to every source record in Zendesk or Intercom. So when someone asks, \u201cIs this just one district or a pattern?\u201d you can open the cluster and immediately see the evidence: which customers, how many tickets, what the common language is, and how it\u2019s trending over time.",
"link": "https://www.onyx.app/3770080-7"
},
{
"text": "David Green: That would help our product team. But you mentioned Sales preventing deal slippage\u2014how does that work in practice?",
"link": "https://www.onyx.app/3770080-8"
},
{
"text": "Megan Foster: This is where it gets really practical for revenue teams. If you have an active late-stage opportunity\u2014say a district evaluating you\u2014and they mention a concern that maps to a known issue, Issue_Tracker can tie that conversation to the relevant cluster. If you\u2019re using Sales_Accelerator as well, it can flag the opportunity as at risk based on the cluster\u2019s severity and status. Then, instead of Sales pinging Support, Support pinging Product, and everyone scrambling, the rep can see: current status, any ETA notes, and\u2014importantly\u2014approved language for what\u2019s safe to communicate.",
"link": "https://www.onyx.app/3770080-9"
},
{
"text": "David Green: Approved language would actually be huge. We\u2019ve had situations where someone on the team over-promises a fix timeline and then we\u2019re stuck doing damage control.",
"link": "https://www.onyx.app/3770080-10"
},
{
"text": "Megan Foster: That\u2019s exactly the scenario we see. The point is to make the \u201cissue object\u201d defensible\u2014so you have a searchable history of what was decided, when, by whom, and what you\u2019ve told customers. It reduces the random Slack archaeology and the \u201cwait, are we fixing this or not?\u201d loop. And because it\u2019s tied to the original tickets and to downstream Jira work, you can confidently say, \u201cThis is in progress,\u201d or \u201cThis is scheduled,\u201d without guessing.",
"link": "https://www.onyx.app/3770080-11"
},
{
"text": "David Green: How heavy is the setup? We\u2019re lean. I don\u2019t want something that takes weeks of taxonomy work before it\u2019s useful.",
"link": "https://www.onyx.app/3770080-12"
},
{
"text": "Megan Foster: Totally. Most teams start light. We typically connect the support channel, pull in a few months of historical tickets, and let clustering surface the obvious themes. Then you add just a handful of rules\u2014like \u201cthese keywords map to this product area\u201d or \u201cflag anything mentioning rostering as integration\u201d\u2014and you\u2019re off. The goal isn\u2019t perfect classification; it\u2019s cutting duplicate triage and making trends visible so you stop underestimating widespread issues hidden across lots of small tickets.",
"link": "https://www.onyx.app/3770080-13"
},
{
"text": "David Green: And does it integrate with Jira? Our engineers live there, and if it\u2019s not connected, adoption will be tough.",
"link": "https://www.onyx.app/3770080-14"
},
{
"text": "Megan Foster: Yes\u2014Issue_Tracker sits between those tools. The cluster stays linked to the downstream Jira work, so anyone can see which engineering ticket is driving resolution, and engineering doesn\u2019t have to change how they operate. The difference is that Product, Support, and Sales all see the same consolidated \u201cissue cluster\u201d view instead of ten separate tickets that don\u2019t look related.",
"link": "https://www.onyx.app/3770080-15"
},
{
"text": "David Green: I can see the value. For Prairielark Education, a lot of our churn risk comes from a couple recurring workflows\u2014district sync, integrations, and reporting. If we could quantify impact and have consistent comms, that\u2019d help.",
"link": "https://www.onyx.app/3770080-16"
},
{
"text": "Megan Foster: That\u2019s a great fit for what Issue_Tracker is designed to do\u2014especially in education where one integration issue can ripple across multiple schools and districts. If you\u2019re open to it, the next step I\u2019d suggest is a 30-minute working session where we look at a sample of your recent support tickets\u2014just a small export or read-only connection\u2014and we\u2019ll show you what clusters it creates, how the merge/split workflow works, and how a late-stage \u201cat-risk\u201d deal would get linked to an issue with safe-to-say messaging.",
"link": "https://www.onyx.app/3770080-17"
},
{
"text": "David Green: That sounds reasonable. I\u2019d want our head of Support looped in for that, since she\u2019d be the daily user.",
"link": "https://www.onyx.app/3770080-18"
},
{
"text": "Megan Foster: Perfect. If you invite her, we\u2019ll tailor it to both perspectives: Support triage efficiency and Sales/CS risk reduction. Do you have availability early next week\u2014maybe Tuesday or Wednesday afternoon?",
"link": "https://www.onyx.app/3770080-19"
},
{
"text": "David Green: Wednesday afternoon could work. Send a couple time options and I\u2019ll pull her in.",
"link": "https://www.onyx.app/3770080-20"
},
{
"text": "Megan Foster: Will do. I\u2019ll email two slots for Wednesday, plus a short outline of what we\u2019ll cover and what access we\u2019d need (we can start with minimal, even just a ticket export). Thanks, David\u2014looking forward to showing you how quickly the clustering surfaces the real themes and how it can keep deals from getting stuck on known issues.",
"link": "https://www.onyx.app/3770080-21"
},
{
"text": "David Green: Sounds good, Megan. I\u2019ll watch for the email and we\u2019ll set it up.",
"link": "https://www.onyx.app/3770080-22"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "david_green@prairielark_education.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_5332795",
"semantic_identifier": "5332795 - Jadecircuit Electronics - sales_pitch - Discussion with Liliana Zamora",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-02-08 00:00:00",
"metadata": {
"meeting_date": "2025-02-08 00:00:00",
"duration_min": "27"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-02"
],
"year_month": "2025-02",
"meeting_title": "5332795 - Jadecircuit Electronics - sales_pitch - Discussion with Liliana Zamora",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hi Liliana\u2014James Choi here. Thanks for taking a few minutes. I know you\u2019re busy at Jadecircuit Electronics, so I\u2019ll keep this tight. I\u2019m calling because a lot of product and revenue teams tell us they\u2019re being asked, \u201cWhich industries should we double down on next quarter?\u201d but the data is scattered across CRM, support, and product usage. Does that sound familiar?",
"link": "https://www.onyx.app/7626006-1"
},
{
"text": "Liliana Zamora: Yes, definitely. We\u2019re debating a couple verticals right now, and everyone has a different opinion based on whatever data they\u2019re closest to.",
"link": "https://www.onyx.app/7626006-2"
},
{
"text": "James Choi: That\u2019s exactly the scenario Customer_Analyzer is built for. It\u2019s a SaaS tool that pulls together signals like CRM fields, onboarding surveys, product usage events, and support tickets\u2014and then it turns that into practical outputs you can act on. The two that matter most for a quick win are: segment fit scoring and positioning recommendations. Have you already standardized on an ICP, or is it still evolving?",
"link": "https://www.onyx.app/7626006-3"
},
{
"text": "Liliana Zamora: We have an ICP on paper, but in reality we keep bending it. And support load has been a big issue\u2014some segments churn less, but they\u2019re heavy on tickets.",
"link": "https://www.onyx.app/7626006-4"
},
{
"text": "James Choi: Perfect example. One common use case we see is prioritizing which industries to target next quarter using retention and support burden signals\u2014not just revenue potential. Customer_Analyzer can take retention, expansion, support volume, and even ticket themes, and create a fit score by segment so you can see, \u201cThis industry retains well but costs us a ton in support,\u201d versus \u201cThis one is lower support and faster time-to-value.\u201d",
"link": "https://www.onyx.app/7626006-5"
},
{
"text": "Liliana Zamora: That\u2019s interesting. We currently look at retention in a dashboard and support in another system, but we don\u2019t really combine them in a disciplined way.",
"link": "https://www.onyx.app/7626006-6"
},
{
"text": "James Choi: Right\u2014and when those aren\u2019t combined, decisions become debate-driven. What\u2019s nice is the fit score isn\u2019t a black box you can\u2019t explain. It\u2019s tied to drivers\u2014like \u201cintegrates with X,\u201d \u201cuses feature Y weekly,\u201d \u201ctickets per account,\u201d \u201ctime to onboard,\u201d that kind of thing\u2014so when you present it internally, you can defend why one segment is a better bet next quarter.",
"link": "https://www.onyx.app/7626006-7"
},
{
"text": "Liliana Zamora: Okay. How does it handle messaging, though? Because even if we pick a segment, our marketing team struggles to translate that into a clear story.",
"link": "https://www.onyx.app/7626006-8"
},
{
"text": "James Choi: That leads into the second feature I think you\u2019d care about: positioning recommendations. Once you pick the segments you want to prioritize, Customer_Analyzer generates segment-specific messaging blocks\u2014like a value proposition, top use cases, key differentiators, proof points to collect, and even objection handling. It\u2019s grounded in observed customer language, so you\u2019re not guessing at what resonates.",
"link": "https://www.onyx.app/7626006-9"
},
{
"text": "Liliana Zamora: When you say \u201cobserved customer language,\u201d where is that coming from?",
"link": "https://www.onyx.app/7626006-10"
},
{
"text": "James Choi: A few places\u2014support tickets are a big one because they contain the actual phrases customers use when they\u2019re frustrated or when something is working. It can also pull in onboarding surveys, interview notes if you have them, and even sales notes in CRM. The output is editable\u2014think website hero copy variants, sales deck bullets, and email openers\u2014so your team can refine it, but you\u2019re starting from evidence-based drafts rather than a blank page.",
"link": "https://www.onyx.app/7626006-11"
},
{
"text": "Liliana Zamora: That would help. We\u2019ve been rewriting messaging every quarter and it\u2019s not clear what\u2019s actually improving performance.",
"link": "https://www.onyx.app/7626006-12"
},
{
"text": "James Choi: Exactly. And because the recommendations are linked back to the underlying drivers and evidence, your team can see \u201cWe\u2019re claiming this differentiator because accounts in this segment repeatedly cite it and have higher retention,\u201d versus \u201cWe think it sounds good.\u201d That tends to speed up alignment between product, marketing, and sales\u2014less back-and-forth, fewer opinion wars.",
"link": "https://www.onyx.app/7626006-13"
},
{
"text": "Liliana Zamora: Makes sense. What would you need from us to get something useful out of it? I\u2019m always wary of tools that take months to set up.",
"link": "https://www.onyx.app/7626006-14"
},
{
"text": "James Choi: Totally fair. For an initial pass, we typically start lightweight: export of a few CRM fields for segmentation, a basic retention/churn view by industry, and support ticket metadata plus a sample of ticket text. If product usage events are accessible, great, but you can get value even without perfect instrumentation. The goal for the first couple weeks is to answer one question: \u201cWhich industries should Jadecircuit target next quarter when you factor in retention and support burden?\u201d",
"link": "https://www.onyx.app/7626006-15"
},
{
"text": "Liliana Zamora: If we could answer that credibly, that\u2019s a big win. We\u2019re planning next quarter\u2019s campaigns now.",
"link": "https://www.onyx.app/7626006-16"
},
{
"text": "James Choi: Great timing then. Let me ask: who usually owns that decision\u2014marketing, product, revenue leadership?",
"link": "https://www.onyx.app/7626006-17"
},
{
"text": "Liliana Zamora: It\u2019s a mix. Marketing proposes, sales pushes back, and product cares because support load becomes their problem too.",
"link": "https://www.onyx.app/7626006-18"
},
{
"text": "James Choi: That\u2019s the classic triangle. In the full walkthrough, I\u2019d show you two things: first, how the fit score looks by industry with drivers like retention and support burden weighted in; second, how the positioning recommendations turn the \u201cwhy this segment\u201d into \u201chow we talk to them.\u201d The outcome is you can go into a planning meeting with a prioritized list and a messaging starter kit.",
"link": "https://www.onyx.app/7626006-19"
},
{
"text": "Liliana Zamora: I like the idea of having the objection-handling piece too. Sales always asks for that, and marketing never has time to produce it.",
"link": "https://www.onyx.app/7626006-20"
},
{
"text": "James Choi: Exactly\u2014and the objection handling is tied to real patterns we see in your signals. For example, if one segment consistently pushes back on implementation time, the playbook will suggest framing and proof points to collect\u2014like \u201ctime-to-value in X days\u201d or \u201cintegration checklist\u201d\u2014so you can proactively address it.",
"link": "https://www.onyx.app/7626006-21"
},
{
"text": "Liliana Zamora: This is sounding relevant. What does the next step look like?",
"link": "https://www.onyx.app/7626006-22"
},
{
"text": "James Choi: The next step is a full 30\u201345 minute call where I can show a sample workflow end-to-end and we can map it to your data sources. If it\u2019s helpful, we can also outline a quick pilot focused on your next-quarter industry prioritization\u2014one question, one deliverable. Would you be open to looping in whoever owns support analytics or your support ops lead for that call?",
"link": "https://www.onyx.app/7626006-23"
},
{
"text": "Liliana Zamora: Yes, I can bring our support ops manager. And maybe someone from marketing ops.",
"link": "https://www.onyx.app/7626006-24"
},
{
"text": "James Choi: Perfect. How\u2019s your calendar early next week\u2014Tuesday or Wednesday afternoon?",
"link": "https://www.onyx.app/7626006-25"
},
{
"text": "Liliana Zamora: Wednesday afternoon could work. Around 2:00?",
"link": "https://www.onyx.app/7626006-26"
},
{
"text": "James Choi: Wednesday at 2:00 works on my end. I\u2019ll send an invite for 45 minutes. To make it productive, I\u2019ll include a short checklist of the fields and exports that help\u2014CRM segmentation fields, retention by industry, and support ticket data. If you can\u2019t pull everything by then, no worries\u2014we can still walk through and identify gaps.",
"link": "https://www.onyx.app/7626006-27"
},
{
"text": "Liliana Zamora: Sounds good. Send the checklist and I\u2019ll see what we can gather.",
"link": "https://www.onyx.app/7626006-28"
},
{
"text": "James Choi: Will do. Last quick question\u2014when you think about \u201csupport burden,\u201d is it mainly ticket volume, severity, or something like time-to-resolution?",
"link": "https://www.onyx.app/7626006-29"
},
{
"text": "Liliana Zamora: All three, but volume and time-to-resolution are the two we track most consistently.",
"link": "https://www.onyx.app/7626006-30"
},
{
"text": "James Choi: Great\u2014that\u2019s enough to start. Thanks, Liliana. I\u2019ll send the calendar invite and prep materials today, and we\u2019ll use the Wednesday call to see if Customer_Analyzer can give Jadecircuit a clear segment priority and messaging direction for next quarter.",
"link": "https://www.onyx.app/7626006-31"
},
{
"text": "Liliana Zamora: Appreciate it, James. Talk Wednesday.",
"link": "https://www.onyx.app/7626006-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "liliana_zamora@jadecircuit_electronics.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_8160901",
"semantic_identifier": "8160901 - Northwind Parcel Services - sales_pitch - Discussion with Charles Arnold",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-02-05 00:00:00",
"metadata": {
"meeting_date": "2025-02-05 00:00:00",
"duration_min": "26"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-02"
],
"year_month": "2025-02",
"meeting_title": "8160901 - Northwind Parcel Services - sales_pitch - Discussion with Charles Arnold",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hi Charles\u2014James Choi here. Thanks for taking a few minutes. I know you\u2019re busy, so I\u2019ll keep this tight. I work with teams that are trying to get really crisp on who their best customers are and how to message differently to each segment. Does that map to anything you\u2019re working on at Northwind Parcel Services right now?",
"link": "https://www.onyx.app/1222730-1"
},
{
"text": "Charles Arnold: Hey James, yeah\u2014definitely. We\u2019ve been debating who we should be targeting more aggressively. We serve a pretty wide range, and messaging gets generic fast.",
"link": "https://www.onyx.app/1222730-2"
},
{
"text": "James Choi: That\u2019s exactly what I wanted to talk about. The product is called Customer_Analyzer. In simple terms, it pulls together the customer signals you already have\u2014CRM fields, onboarding surveys, product usage, support tickets, even notes from calls\u2014and turns that into two things: a fit score per account/segment, and positioning recommendations you can actually use in marketing and sales.",
"link": "https://www.onyx.app/1222730-3"
},
{
"text": "Charles Arnold: Fit score as in\u2026 \u201cthis customer is a good match\u201d?",
"link": "https://www.onyx.app/1222730-4"
},
{
"text": "James Choi: Right. It\u2019s a 0\u2013100 fit score, but the key is the \u201cwhy.\u201d You define the fit drivers that matter for your business\u2014things like company size, industry, problem severity, willingness to pay, time-to-value, adoption patterns, support burden, churn risk signals. Then it shows you the driver breakdown so you can see what\u2019s pushing a segment up or down.",
"link": "https://www.onyx.app/1222730-5"
},
{
"text": "Charles Arnold: We\u2019ve tried some scoring in the CRM before, but it got political. Everyone had opinions.",
"link": "https://www.onyx.app/1222730-6"
},
{
"text": "James Choi: Totally common. Where Customer_Analyzer helps is it ties the scoring to observed outcomes\u2014conversion, retention, expansion\u2014so it\u2019s not just \u201csales thinks X,\u201d it\u2019s \u201caccounts with these attributes convert faster and churn less.\u201d And it can compare segments side by side\u2014like SMB vs mid-market\u2014so you can stop arguing in the abstract and look at the data.",
"link": "https://www.onyx.app/1222730-7"
},
{
"text": "Charles Arnold: SMB vs mid-market is actually a live discussion for us. We\u2019ve been wondering if we should split our website messaging and outbound more explicitly.",
"link": "https://www.onyx.app/1222730-8"
},
{
"text": "James Choi: Perfect use case. One of the most common things customers do with us is create segment-specific landing pages and outbound messaging for two ICP tiers\u2014exactly SMB vs mid-market. The workflow looks like: we ingest your signals, we compute the fit score by segment, then we generate positioning recommendations per segment\u2014value props, differentiators, proof points, and objection handling\u2014so marketing and reps aren\u2019t re-inventing the narrative every time.",
"link": "https://www.onyx.app/1222730-9"
},
{
"text": "Charles Arnold: When you say \u201cpositioning recommendations,\u201d is it like generic AI copy, or is it actually grounded in our data?",
"link": "https://www.onyx.app/1222730-10"
},
{
"text": "James Choi: Grounded. It\u2019s not \u201chere are random slogans.\u201d It\u2019s \u201cfor mid-market accounts with these characteristics, the drivers that correlate with retention and expansion are A and B, so lead with outcomes X and Y; here are proof points you can pull from your support data or usage patterns; and here are the objections we see in interview notes and tickets, with suggested responses.\u201d It\u2019s meant to be practical for real campaigns and sequences.",
"link": "https://www.onyx.app/1222730-11"
},
{
"text": "Charles Arnold: Interesting. What kind of inputs do you typically need to get started? We have Salesforce, product usage events, Zendesk. Some of our onboarding survey data is messy.",
"link": "https://www.onyx.app/1222730-12"
},
{
"text": "James Choi: That\u2019s a normal starting point: Salesforce + product usage + Zendesk gets you a lot of signal. We don\u2019t need perfection on day one. Typically we start with a handful of fit drivers you already trust\u2014say size, industry, time-to-value, adoption, support burden\u2014and let the model show how those relate to outcomes. Then we refine as you add cleaner survey fields or better tagging over time.",
"link": "https://www.onyx.app/1222730-13"
},
{
"text": "Charles Arnold: How long does it take before we get something useful? I\u2019m allergic to long implementations.",
"link": "https://www.onyx.app/1222730-14"
},
{
"text": "James Choi: Same. The goal is quick signal, not a six-month science project. On a first pass, many teams can get initial segment comparisons and a draft ICP recommendation in a couple weeks once data connections are in. Then you use that to run one or two experiments\u2014like updating an SMB landing page versus a mid-market page, or changing outbound talk tracks\u2014and you can see if conversion or response rates move.",
"link": "https://www.onyx.app/1222730-15"
},
{
"text": "Charles Arnold: Okay. What does the \u201crecommended ICP profile\u201d look like in practice?",
"link": "https://www.onyx.app/1222730-16"
},
{
"text": "James Choi: It\u2019s a profile backed by the data you\u2019ve fed in. So instead of \u201cwe think our ICP is mid-market logistics,\u201d it becomes \u201cthe highest-fit accounts have these firmographics, these adoption patterns, lower support burden in these areas, faster time-to-value, and higher expansion rates.\u201d It also flags \u201clooks attractive but actually risky\u201d segments\u2014like accounts that buy but churn or generate heavy support load.",
"link": "https://www.onyx.app/1222730-17"
},
{
"text": "Charles Arnold: That support burden angle is compelling. We have some customers who are loud and expensive.",
"link": "https://www.onyx.app/1222730-18"
},
{
"text": "James Choi: Exactly. Fit isn\u2019t just \u201cwill they buy,\u201d it\u2019s \u201cwill they succeed and renew.\u201d If a segment drives a ton of tickets or slow onboarding, the fit score will reflect that, and the driver breakdown will make it visible. Then you can decide: do we fix the product for them, price differently, or stop targeting them?",
"link": "https://www.onyx.app/1222730-19"
},
{
"text": "Charles Arnold: Makes sense. If we did the SMB vs mid-market messaging split, what would you recommend as the first step?",
"link": "https://www.onyx.app/1222730-20"
},
{
"text": "James Choi: First step is aligning on 6\u201310 fit drivers and the outcomes you care about\u2014probably conversion to paid, retention, and maybe expansion. Then we run Customer_Analyzer to compare SMB vs mid-market on those drivers and outcomes. From there, we produce two sets of positioning: one for SMB, one for mid-market\u2014each with a primary value proposition, top differentiators, proof points you can substantiate, and a short objection-handling section for sales.",
"link": "https://www.onyx.app/1222730-21"
},
{
"text": "Charles Arnold: And we can use that directly for landing pages and sequences?",
"link": "https://www.onyx.app/1222730-22"
},
{
"text": "James Choi: Yes. The deliverable is designed to plug into a landing page brief and an outbound framework. It\u2019s not trying to replace your copywriter or sales enablement\u2014just giving them a data-backed foundation so the messaging isn\u2019t generic.",
"link": "https://www.onyx.app/1222730-23"
},
{
"text": "Charles Arnold: This is relevant. I\u2019d want my head of marketing and our sales ops lead to hear this, though. I\u2019m not the only stakeholder.",
"link": "https://www.onyx.app/1222730-24"
},
{
"text": "James Choi: Totally fair. What I\u2019d propose is a 30\u201345 minute working session next week. We\u2019ll keep it practical: you bring someone from marketing and sales ops, and we\u2019ll map your current segments and data sources, pick the fit drivers, and I\u2019ll show what the SMB vs mid-market outputs look like\u2014fit score, driver breakdown, and the positioning recommendations. If at the end it doesn\u2019t feel actionable, we\u2019ll stop there.",
"link": "https://www.onyx.app/1222730-25"
},
{
"text": "Charles Arnold: That sounds reasonable. Do you need anything from us ahead of time?",
"link": "https://www.onyx.app/1222730-26"
},
{
"text": "James Choi: Just a quick list of your current segmentation hypothesis\u2014how you define SMB vs mid-market today\u2014and which systems hold the data: Salesforce fields you trust, product event source, Zendesk categories. If you can send that, I\u2019ll tailor the demo to Northwind and not do a generic walkthrough.",
"link": "https://www.onyx.app/1222730-27"
},
{
"text": "Charles Arnold: Yeah, I can do that. Next week\u2014Tuesday or Wednesday afternoon works for me.",
"link": "https://www.onyx.app/1222730-28"
},
{
"text": "James Choi: Great. I have Wednesday at 2:00 pm or 3:30 pm your time. Which is better?",
"link": "https://www.onyx.app/1222730-29"
},
{
"text": "Charles Arnold: Let\u2019s do Wednesday at 3:30. I\u2019ll invite marketing and sales ops.",
"link": "https://www.onyx.app/1222730-30"
},
{
"text": "James Choi: Perfect\u2014Wednesday 3:30 it is. I\u2019ll send a calendar invite and a short pre-read with the fit driver examples so your team can react. Appreciate the time, Charles.",
"link": "https://www.onyx.app/1222730-31"
},
{
"text": "Charles Arnold: Thanks, James. Looking forward to it.",
"link": "https://www.onyx.app/1222730-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "charles_arnold@northwind_parcel_services.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_1148499",
"semantic_identifier": "1148499 - Marinerock Outdoor Gear - sales_pitch - Discussion with Dustin Dean",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-03-26 00:00:00",
"metadata": {
"meeting_date": "2025-03-26 00:00:00",
"duration_min": "17"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-03"
],
"year_month": "2025-03",
"meeting_title": "1148499 - Marinerock Outdoor Gear - sales_pitch - Discussion with Dustin Dean",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hi Dustin\u2014James Choi here. Thanks for taking a few minutes. Did I catch you at an okay time?",
"link": "https://www.onyx.app/9158769-1"
},
{
"text": "Dustin Dean: Yeah, I\u2019ve got a few minutes. What\u2019s up?",
"link": "https://www.onyx.app/9158769-2"
},
{
"text": "James Choi: Great. I\u2019ll be quick. I\u2019m reaching out because we\u2019ve been talking with a lot of sales leaders who are tired of end-of-quarter forecast surprises\u2014where the CRM says \u201ccommit,\u201d but reality says \u201cnot happening.\u201d We built something called Sales_Accelerator that sits on top of Salesforce or HubSpot and turns your pipeline signals into an explainable forecast and clear \u201cwhat to do next\u201d guidance. Curious\u2014how are you forecasting today at Marinerock Outdoor Gear?",
"link": "https://www.onyx.app/9158769-3"
},
{
"text": "Dustin Dean: Mostly Salesforce, but we still end up doing a spreadsheet rollup for leadership. It\u2019s\u2026 not fun. And accuracy depends on who updated what.",
"link": "https://www.onyx.app/9158769-4"
},
{
"text": "James Choi: That\u2019s exactly the pattern we see. The core thing Sales_Accelerator does is combine your CRM stage data with actual activity signals\u2014emails, calls, meetings\u2014plus cycle-time benchmarks to tell you what\u2019s real. Instead of \u201cwe feel good,\u201d it gives you a commit / best-case / pipeline view with drivers you can point to, like: stage age, last touch, whether a next meeting is scheduled, and engagement trend.",
"link": "https://www.onyx.app/9158769-5"
},
{
"text": "Dustin Dean: Okay. We do look at stage age, but it\u2019s manual. And \u201cnext meeting scheduled\u201d is a constant battle.",
"link": "https://www.onyx.app/9158769-6"
},
{
"text": "James Choi: Right\u2014and the manual part is where it breaks down. One feature we lean on heavily is our sales_forecast module. It flags slippage risk automatically. For example, if a deal is marked commit but it\u2019s older than your typical cycle-time for that stage, hasn\u2019t had a meaningful touch in X days, and there\u2019s no next meeting on the calendar\u2014Sales_Accelerator highlights it as \u201cweakening,\u201d and it tells you why. That lets your VP Sales run a targeted deal review instead of spending an hour debating whose gut is right.",
"link": "https://www.onyx.app/9158769-7"
},
{
"text": "Dustin Dean: That would be helpful. We do deal reviews, but it\u2019s often a lot of storytelling.",
"link": "https://www.onyx.app/9158769-8"
},
{
"text": "James Choi: Exactly. And the goal isn\u2019t to replace the rep\u2019s context\u2014it\u2019s to give a consistent system so the conversation starts with facts. You can drill from pipeline-level forecast down to the opportunity and see, \u201cThis is slipping because the stage is aging and activity dropped,\u201d or \u201cThis looks solid because there\u2019s been steady engagement and a next step is locked.\u201d",
"link": "https://www.onyx.app/9158769-9"
},
{
"text": "Dustin Dean: How hard is this to set up? We don\u2019t want another tool that requires a huge admin project.",
"link": "https://www.onyx.app/9158769-10"
},
{
"text": "James Choi: Totally fair. It\u2019s designed as an execution layer on top of your existing CRM, so it\u2019s not asking you to rebuild your process. Typically we connect to Salesforce/HubSpot, map your stages, and pull in activity signals. From there, Sales_Accelerator starts producing the forecast views and diagnostics\u2014stage conversion, slippage trends, and segment-level breakdowns by rep or source. The point is to get you out of spreadsheets and into a forecast you can explain to leadership.",
"link": "https://www.onyx.app/9158769-11"
},
{
"text": "Dustin Dean: You mentioned \u201cexplainable.\u201d What does that look like in practice?",
"link": "https://www.onyx.app/9158769-12"
},
{
"text": "James Choi: Great question. Every forecast rollup has the underlying drivers visible\u2014think \u201cwhy we believe this is commit.\u201d You can see the impact of stage age, last touch, next meeting scheduled, engagement signals, and historical win rate for similar deals. So if your leadership asks \u201cwhy did commit drop this week,\u201d you can answer without hunting through notes or chasing reps.",
"link": "https://www.onyx.app/9158769-13"
},
{
"text": "Dustin Dean: That would save time. Our leadership always wants to know what changed and why.",
"link": "https://www.onyx.app/9158769-14"
},
{
"text": "James Choi: Exactly. And you can do scenario planning too. For instance: \u201cIf we add 20 new SQLs in our top segment, what\u2019s the expected impact on the quarter?\u201d The forecast uses your historical conversion and cycle-time benchmarks so you\u2019re not guessing. It\u2019s especially useful when you\u2019re deciding whether you need more pipeline coverage or you should focus on progressing what you already have.",
"link": "https://www.onyx.app/9158769-15"
},
{
"text": "Dustin Dean: Interesting. We\u2019ve been trying to get more disciplined about pipeline math instead of just pushing for \u201cmore deals.\u201d",
"link": "https://www.onyx.app/9158769-16"
},
{
"text": "James Choi: That discipline is where teams see the biggest change. And the use case I think is most relevant to you: a VP Sales improving quarter-end accuracy by replacing manual spreadsheet rollups with an explainable forecast that highlights which commit deals are actually weakening\u2014like \u201cstage age + no next meeting + low engagement\u201d\u2014and then running targeted deal reviews on just those deals.",
"link": "https://www.onyx.app/9158769-17"
},
{
"text": "Dustin Dean: That\u2019s basically our pain. We spend a lot of time chasing updates, and then we still get surprised.",
"link": "https://www.onyx.app/9158769-18"
},
{
"text": "James Choi: Makes sense. Quick check\u2014how often are you updating your forecast and running reviews today? Weekly? Twice a week near EOM?",
"link": "https://www.onyx.app/9158769-19"
},
{
"text": "Dustin Dean: Weekly, then it becomes more frequent near the end. And it\u2019s a scramble.",
"link": "https://www.onyx.app/9158769-20"
},
{
"text": "James Choi: Got it. Sales_Accelerator is built to reduce that scramble by keeping the risk signals continuously updated\u2014so you\u2019re not discovering on Friday that half your commit deals have no next step. It\u2019s more of a proactive \u201chere are the deals at risk and what\u2019s driving it\u201d view, so your reviews can be shorter and more focused.",
"link": "https://www.onyx.app/9158769-21"
},
{
"text": "Dustin Dean: Sounds good, but I\u2019m wondering\u2014does it just tell you \u201cbad\u201d or does it actually help you act?",
"link": "https://www.onyx.app/9158769-22"
},
{
"text": "James Choi: Great point. We\u2019re not trying to be just another dashboard. When a deal is at risk, it doesn\u2019t just flag it\u2014it pushes a recommended next action, like \u201cschedule next meeting,\u201d \u201cre-engage stakeholder,\u201d or \u201cconfirm timeline,\u201d based on the pattern it\u2019s seeing. But for this first conversation, the simplest win is forecast accuracy and removing spreadsheet rollups\u2014because that\u2019s measurable quickly.",
"link": "https://www.onyx.app/9158769-23"
},
{
"text": "Dustin Dean: Yeah, if we can kill the spreadsheet, people will be happy.",
"link": "https://www.onyx.app/9158769-24"
},
{
"text": "James Choi: Exactly. To make this concrete, what I\u2019d suggest is a deeper session where we look at your current forecast workflow, your CRM stages, and what signals you track today. Then we can show you what Sales_Accelerator would flag as weakening in your commit, and what the forecast would look like by segment and rep. Does that sound worth a longer working session?",
"link": "https://www.onyx.app/9158769-25"
},
{
"text": "Dustin Dean: Yeah, I\u2019d be open to that. I\u2019d want our ops lead involved too.",
"link": "https://www.onyx.app/9158769-26"
},
{
"text": "James Choi: Perfect. If you can bring your sales ops lead and maybe whoever owns forecasting, I\u2019ll bring a quick demo focused on sales_forecast\u2014commit/best-case views, slippage diagnostics, and the explainable drivers. We\u2019ll keep it practical: your stages, your cycle times, and how you\u2019d replace the spreadsheet.",
"link": "https://www.onyx.app/9158769-27"
},
{
"text": "Dustin Dean: That works. How long are you thinking?",
"link": "https://www.onyx.app/9158769-28"
},
{
"text": "James Choi: Typically 30 minutes. If we\u2019re getting into more detail, we can extend to 45, but we can start with 30. Are you free Thursday morning or Friday afternoon?",
"link": "https://www.onyx.app/9158769-29"
},
{
"text": "Dustin Dean: Thursday morning could work. Around 10?",
"link": "https://www.onyx.app/9158769-30"
},
{
"text": "James Choi: Thursday at 10 works on my end. I\u2019ll send a calendar invite for 10:00\u201310:30 and include a short pre-read\u2014just a couple questions about your CRM and how you define commit. Sound good?",
"link": "https://www.onyx.app/9158769-31"
},
{
"text": "Dustin Dean: Yeah, send it over. I\u2019ll loop in our ops lead.",
"link": "https://www.onyx.app/9158769-32"
},
{
"text": "James Choi: Great. Thanks, Dustin\u2014appreciate the time today. I\u2019ll get that invite out and we\u2019ll focus the next session on eliminating the spreadsheet and improving forecast accuracy with clear risk signals.",
"link": "https://www.onyx.app/9158769-33"
},
{
"text": "Dustin Dean: Sounds good. Talk Thursday.",
"link": "https://www.onyx.app/9158769-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "dustin_dean@marinerock_outdoor_gear.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_1181903",
"semantic_identifier": "1181903 - Apexnova Solutions - sales_pitch - Discussion with Maria Lane",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-03-30 00:00:00",
"metadata": {
"meeting_date": "2025-03-30 00:00:00",
"duration_min": "26"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-03"
],
"year_month": "2025-03",
"meeting_title": "1181903 - Apexnova Solutions - sales_pitch - Discussion with Maria Lane",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi Maria, thanks for taking a few minutes\u2014I'll keep this tight. I\u2019m Megan with Customer_Analyzer. The reason I reached out is we help product, marketing, and sales teams get really clear on who their best-fit customers are and then turn that into messaging your team can actually use\u2014fast.",
"link": "https://www.onyx.app/4696334-1"
},
{
"text": "Maria Lane: Hi Megan\u2014sure, I have a few minutes. What exactly do you mean by \u201cturn that into messaging\u201d?",
"link": "https://www.onyx.app/4696334-2"
},
{
"text": "Megan Foster: Great question. Customer_Analyzer pulls in the customer signals you already have\u2014CRM fields, onboarding survey answers, product usage events, support tickets, even interview notes\u2014and then generates positioning recommendations for the segments you care about. So instead of debating \u201cwhat should we say to SMB vs mid-market?\u201d you get segment-specific value props, differentiators, proof points to collect, and even objection handling\u2014packaged as editable blocks like website hero copy, sales deck bullets, and outbound email openers.",
"link": "https://www.onyx.app/4696334-3"
},
{
"text": "Maria Lane: That\u2019s interesting. We actually do struggle with tailoring the story. But we\u2019ve tried \u201cICP exercises\u201d before\u2014lots of slides, not much changes.",
"link": "https://www.onyx.app/4696334-4"
},
{
"text": "Megan Foster: Totally hear that. The difference is we don\u2019t stop at a static ICP doc. We link the messaging back to evidence\u2014what the segment is doing in the product, what they\u2019re saying in tickets or calls, what closes in the CRM. Then it produces recommendations you can ship: for example, a landing page hero and three supporting bullets for SMB, and a different set for mid-market, with the underlying \u201cfit drivers\u201d called out so your team trusts it.",
"link": "https://www.onyx.app/4696334-5"
},
{
"text": "Maria Lane: When you say \u201cfit drivers,\u201d what are those?",
"link": "https://www.onyx.app/4696334-6"
},
{
"text": "Megan Foster: They\u2019re the specific signals correlated with success for a segment\u2014like shorter time-to-first-value, higher adoption of a key feature, fewer support escalations, or consistent language in onboarding surveys around a particular outcome. The product uses those to explain *why* a segment is a good fit, and then it turns that into positioning: \u201chere\u2019s the promise,\u201d \u201chere\u2019s what to lead with,\u201d \u201chere\u2019s how to frame pricing/value,\u201d and \u201chere are the top objections we should preempt.\u201d",
"link": "https://www.onyx.app/4696334-7"
},
{
"text": "Maria Lane: Okay. We\u2019re currently splitting messaging by company size\u2014SMB versus mid-market\u2014but it\u2019s messy. Sales says one thing, marketing says another.",
"link": "https://www.onyx.app/4696334-8"
},
{
"text": "Megan Foster: That\u2019s exactly the use case where we see quick wins: create segment-specific landing pages and outbound messaging for two ICP tiers. We can take your existing segments\u2014SMB and mid-market\u2014prioritize them, and generate two sets of messaging blocks. The output is structured so marketing can drop it into a landing page, and sales can copy/paste it into an email opener or deck slide\u2014without reinventing the wheel.",
"link": "https://www.onyx.app/4696334-9"
},
{
"text": "Maria Lane: How much work is it to get set up? We don\u2019t have a lot of bandwidth for yet another tool.",
"link": "https://www.onyx.app/4696334-10"
},
{
"text": "Megan Foster: Low lift to start. We typically begin with just a few sources: your CRM fields for customer profile and outcomes, a slice of onboarding survey responses, and a batch of support ticket themes. If you have product usage events, great, but it\u2019s not required day one. Within that, we can generate initial positioning recommendations quickly, then refine as you add more signals.",
"link": "https://www.onyx.app/4696334-11"
},
{
"text": "Maria Lane: And how do you avoid the \u201cgeneric AI copy\u201d problem? We\u2019ve tested tools that spit out fluffy messaging.",
"link": "https://www.onyx.app/4696334-12"
},
{
"text": "Megan Foster: That\u2019s the big thing we\u2019re careful about. The recommendations are anchored to observed customer language and your own drivers\u2014so you can see, \u201cthis differentiator came from these ticket themes and these win notes,\u201d or \u201cthis value prop aligns with what activated customers do in week one.\u201d Plus, everything is editable, so your team can tweak the blocks while keeping the structure consistent across channels.",
"link": "https://www.onyx.app/4696334-13"
},
{
"text": "Maria Lane: Got it. Can it help with objections specifically? We get different pushback in mid-market than SMB.",
"link": "https://www.onyx.app/4696334-14"
},
{
"text": "Megan Foster: Yes\u2014each segment gets an objection-handling playbook. For example, mid-market might push on security, integrations, or procurement timelines; SMB might push on price or implementation time. The tool recommends how to respond in a way that matches the segment\u2019s priorities, and it suggests proof points to collect\u2014like a security one-pager, integration case studies, or \u201ctime-to-value\u201d metrics\u2014so you\u2019re not guessing.",
"link": "https://www.onyx.app/4696334-15"
},
{
"text": "Maria Lane: That\u2019s compelling. If we did this, what would a \u201cgood first outcome\u201d look like in, say, a couple weeks?",
"link": "https://www.onyx.app/4696334-16"
},
{
"text": "Megan Foster: In two weeks, a realistic first outcome is: two messaging kits\u2014one for SMB, one for mid-market\u2014each with (1) a landing page hero section draft, (2) 3\u20135 sales deck bullets, (3) a couple outbound email openers, and (4) the top objections and proof points to gather. Then you can run a simple test: update your two landing pages and give the outbound openers to one SDR pod, and measure reply rates or conversion lift.",
"link": "https://www.onyx.app/4696334-17"
},
{
"text": "Maria Lane: We\u2019re actually about to refresh our website and we\u2019ve been arguing about the hero copy. So the timing is good.",
"link": "https://www.onyx.app/4696334-18"
},
{
"text": "Megan Foster: Perfect. That\u2019s a very practical place to start because it forces alignment. And since it\u2019s segment-specific, you\u2019re not stuck with one \u201ccompromise\u201d message that\u2019s too broad to resonate.",
"link": "https://www.onyx.app/4696334-19"
},
{
"text": "Maria Lane: Who on our side usually owns this? Marketing? Product marketing? Sales enablement?",
"link": "https://www.onyx.app/4696334-20"
},
{
"text": "Megan Foster: Typically product marketing or growth marketing owns the landing page output, and sales enablement or a revenue leader partners on the objection handling and outbound blocks. But it works best when we have one point person\u2014could be you\u2014who can pull in quick feedback from sales for 15 minutes.",
"link": "https://www.onyx.app/4696334-21"
},
{
"text": "Maria Lane: Okay. What do you need from me to see if this is a fit for Apexnova?",
"link": "https://www.onyx.app/4696334-22"
},
{
"text": "Megan Foster: I\u2019d love to schedule a full 30-minute working session. In that call, we\u2019ll map your current SMB vs mid-market definitions, identify the signals you already have, and I\u2019ll show what the positioning recommendation output looks like end-to-end\u2014landing page blocks, outbound openers, objections\u2014so you can judge whether it\u2019s actionable for your team.",
"link": "https://www.onyx.app/4696334-23"
},
{
"text": "Maria Lane: Yes, I\u2019m open to that. Can you also cover how you handle data access and privacy? We have some sensitivity around support tickets and notes.",
"link": "https://www.onyx.app/4696334-24"
},
{
"text": "Megan Foster: Absolutely\u2014happy to walk through access controls, what we ingest, and how you can start with a limited dataset if needed. We can also begin with non-sensitive fields and survey data, and add other sources later once you\u2019re comfortable.",
"link": "https://www.onyx.app/4696334-25"
},
{
"text": "Maria Lane: Great. Let\u2019s do the deeper session. Do you have availability this week?",
"link": "https://www.onyx.app/4696334-26"
},
{
"text": "Megan Foster: I do. I can do Thursday at 11am or Friday at 2pm your time. Which works better?",
"link": "https://www.onyx.app/4696334-27"
},
{
"text": "Maria Lane: Friday at 2pm works.",
"link": "https://www.onyx.app/4696334-28"
},
{
"text": "Megan Foster: Perfect\u2014I'll send a calendar invite for Friday at 2pm, plus a short prep list: the CRM fields you use for segmenting, a sample of onboarding survey questions, and any notes on how you currently differentiate SMB vs mid-market. Sound good?",
"link": "https://www.onyx.app/4696334-29"
},
{
"text": "Maria Lane: Sounds good. Send it over and I\u2019ll loop in our product marketer as well.",
"link": "https://www.onyx.app/4696334-30"
},
{
"text": "Megan Foster: Great\u2014thanks, Maria. I\u2019ll get that invite out right now, and we\u2019ll make Friday very concrete with draft messaging you can react to.",
"link": "https://www.onyx.app/4696334-31"
},
{
"text": "Maria Lane: Looking forward to it. Thanks, Megan.",
"link": "https://www.onyx.app/4696334-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "maria_lane@apexnova_solutions.onyx.app"
}
]
}

View File

@@ -0,0 +1,161 @@
{
"id": "FIREFLIES_3091983",
"semantic_identifier": "3091983 - Kestrelbloom Agriculture - sales_pitch - Discussion with Sami Hamdan",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-03-30 00:00:00",
"metadata": {
"meeting_date": "2025-03-30 00:00:00",
"duration_min": "31"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-03"
],
"year_month": "2025-03",
"meeting_title": "3091983 - Kestrelbloom Agriculture - sales_pitch - Discussion with Sami Hamdan",
"organizer_email": "camila_vega@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Camila Vega: Hi Sami\u2014Camila Vega here. Thanks for taking a few minutes. I\u2019ll be quick: I\u2019m calling because we\u2019ve been helping revenue teams get a more reliable forecast and clearer \u201cwhat should I do next\u201d guidance without ripping out Salesforce. Is now still okay?",
"link": "https://www.onyx.app/7371680-1"
},
{
"text": "Sami Hamdan: Yep, I\u2019ve got about five minutes. What are you seeing that\u2019s relevant to us at Kestrelbloom?",
"link": "https://www.onyx.app/7371680-2"
},
{
"text": "Camila Vega: Perfect. The product is Sales_Accelerator\u2014it sits on top of Salesforce or HubSpot and turns what\u2019s already in your CRM plus activity signals\u2014emails, calls, meetings\u2014into an explainable forecast and next-step recommendations per opportunity. Most teams we talk to have two pain points: forecasts that swing late in the quarter, and reps who aren\u2019t sure what action will actually move a deal forward. Does either sound familiar?",
"link": "https://www.onyx.app/7371680-3"
},
{
"text": "Sami Hamdan: Both, honestly. Forecast accuracy is a constant battle. And coaching reps to run consistent follow-ups is another.",
"link": "https://www.onyx.app/7371680-4"
},
{
"text": "Camila Vega: That\u2019s exactly where Sales_Accelerator focuses. On forecasting, it doesn\u2019t just look at stage\u2014it weights signals like stage age, last touch, whether a next meeting is scheduled, and historical win rates for similar deals. Then it produces commit/best-case/pipeline views and flags slippage risk early\u2014so you\u2019re not discovering in week 12 that half your \u201ccommit\u201d deals are stale.",
"link": "https://www.onyx.app/7371680-5"
},
{
"text": "Sami Hamdan: We have some dashboards for that already. How is this different from what Salesforce reports can do?",
"link": "https://www.onyx.app/7371680-6"
},
{
"text": "Camila Vega: Great question. The difference is it combines CRM stage data with real activity and cycle-time benchmarks, and it explains the \u201cwhy\u201d behind each forecast number. So instead of \u201cthis is in stage 3,\u201d you\u2019ll see drivers like \u201cstage age is 2x benchmark,\u201d \u201cno customer meeting booked,\u201d \u201clast outbound was 10 days ago,\u201d or \u201csimilar deals from this segment historically convert at X%.\u201d That makes it coachable, not just reportable.",
"link": "https://www.onyx.app/7371680-7"
},
{
"text": "Sami Hamdan: The explainable part is interesting. Our leadership always asks why the model says what it says. But we also have an ICP project running\u2014how does that play in?",
"link": "https://www.onyx.app/7371680-8"
},
{
"text": "Camila Vega: Perfect timing. Sales_Accelerator is designed to consume fit scoring and positioning work from a system like Customer_Analyzer, not duplicate it. If you already have fit tiers or positioning blocks, we can use them as a weighting factor in forecast and prioritization\u2014so your team isn\u2019t just chasing \u201clate-stage\u201d deals, they\u2019re focusing on late-stage deals that match your best-fit profile.",
"link": "https://www.onyx.app/7371680-9"
},
{
"text": "Sami Hamdan: Okay. What about the rep workflow? If it\u2019s another tool they have to check, adoption will be rough.",
"link": "https://www.onyx.app/7371680-10"
},
{
"text": "Camila Vega: Totally fair. The second piece I wanted to highlight is best-next-action guidance inside the deal flow. One common use case is after discovery: the AE gets a follow-up recommendation that drafts a recap email, pulls two approved proof points from your positioning library, and helps create a mutual action plan with due dates and stakeholders\u2014so multi-threading and next steps are standardized.",
"link": "https://www.onyx.app/7371680-11"
},
{
"text": "Sami Hamdan: That would help. We see deals stall because the follow-up is inconsistent, and the mutual plan lives in someone\u2019s head.",
"link": "https://www.onyx.app/7371680-12"
},
{
"text": "Camila Vega: Exactly. And when the mutual action plan isn\u2019t progressing\u2014no next meeting, no stakeholder added, tasks slipping\u2014Sales_Accelerator triggers an early warning that the deal is at risk. That\u2019s when managers can intervene with something concrete: \u201cBook the technical review with Ops by Friday,\u201d rather than \u201cpush harder.\u201d",
"link": "https://www.onyx.app/7371680-13"
},
{
"text": "Sami Hamdan: How much setup are we talking? We can\u2019t afford a multi-month implementation.",
"link": "https://www.onyx.app/7371680-14"
},
{
"text": "Camila Vega: Usually lightweight. Because it sits on top of your CRM, we start by connecting Salesforce, mapping a few core fields, and pulling your activity signals. Then we configure cycle-time benchmarks based on your historical data. If you have fit tiers from Customer_Analyzer, we connect those too. The goal is to get you useful forecasts and risk flags quickly\u2014then iterate with sales ops and a pilot group.",
"link": "https://www.onyx.app/7371680-15"
},
{
"text": "Sami Hamdan: If we did a pilot, who typically uses it day to day?",
"link": "https://www.onyx.app/7371680-16"
},
{
"text": "Camila Vega: Day to day: AEs and SDRs see deal-level guidance\u2014what to do next, what\u2019s missing, and drafted follow-ups. Managers and sales ops use the forecast views, slippage diagnostics, and scenario planning\u2014like \u201cif we add 20 SQLs in our Tier 1 ICP, what\u2019s the expected impact on the quarter?\u201d It gives you a lever to model outcomes, not just hope.",
"link": "https://www.onyx.app/7371680-17"
},
{
"text": "Sami Hamdan: Scenario planning sounds useful for us. We\u2019re always debating whether to push for more top-of-funnel or focus on conversion.",
"link": "https://www.onyx.app/7371680-18"
},
{
"text": "Camila Vega: Exactly\u2014this makes that debate data-driven. And because the forecast is explainable, you can defend it in your internal reviews: \u201cThese are the specific deals at risk and the drivers,\u201d plus \u201cthese activities are most correlated with movement for our Tier 1 accounts.\u201d",
"link": "https://www.onyx.app/7371680-19"
},
{
"text": "Sami Hamdan: What do you need from me to see if this is a fit?",
"link": "https://www.onyx.app/7371680-20"
},
{
"text": "Camila Vega: Two things for a next step. First, I\u2019d love to schedule a longer working session\u201430 to 45 minutes\u2014where we look at your current forecast process, which signals you trust, and where deals tend to slip. Second, if you\u2019re open to it, we can prep a light data checklist\u2014fields and activity sources\u2014so we can show you what Sales_Accelerator would flag on a handful of opportunities.",
"link": "https://www.onyx.app/7371680-21"
},
{
"text": "Sami Hamdan: That sounds reasonable. Who should be on that session on our side?",
"link": "https://www.onyx.app/7371680-22"
},
{
"text": "Camila Vega: Ideally you, whoever owns sales ops or forecasting, and maybe one frontline manager or AE who can speak to the follow-up workflow after discovery. That way we cover both: forecast accuracy and rep execution.",
"link": "https://www.onyx.app/7371680-23"
},
{
"text": "Sami Hamdan: Okay. I can bring our sales ops lead and one of our regional managers. When are you thinking?",
"link": "https://www.onyx.app/7371680-24"
},
{
"text": "Camila Vega: I have Thursday at 11:00 a.m. or Friday at 2:00 p.m. your time. We\u2019ll keep it practical: current state, what \u201cgood\u201d looks like for Kestrelbloom, and a walkthrough of forecast drivers plus the post-discovery follow-up recommendations.",
"link": "https://www.onyx.app/7371680-25"
},
{
"text": "Sami Hamdan: Let\u2019s do Friday at 2:00. Send an invite and the checklist, and I\u2019ll loop them in.",
"link": "https://www.onyx.app/7371680-26"
},
{
"text": "Camila Vega: Perfect\u2014Friday at 2:00 it is. I\u2019ll send the calendar invite with a short agenda and a data checklist. Before we hop, is there one metric leadership cares most about\u2014forecast accuracy, slippage reduction, or improving conversion in a specific stage?",
"link": "https://www.onyx.app/7371680-27"
},
{
"text": "Sami Hamdan: Forecast accuracy and reducing late-stage slippage. Those are the two that keep coming up.",
"link": "https://www.onyx.app/7371680-28"
},
{
"text": "Camila Vega: Got it. I\u2019ll tailor the session to those, and we\u2019ll focus on the explainable forecast drivers and the at-risk alerts tied to concrete next actions. Thanks, Sami\u2014looking forward to Friday.",
"link": "https://www.onyx.app/7371680-29"
},
{
"text": "Sami Hamdan: Sounds good. Talk then.",
"link": "https://www.onyx.app/7371680-30"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "camila_vega@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "sami_hamdan@kestrelbloom_agriculture.onyx.app"
}
]
}

View File

@@ -0,0 +1,153 @@
{
"id": "FIREFLIES_5536128",
"semantic_identifier": "5536128 - Unitypeak Nonprofits - sales_pitch - Discussion with Fernando Chapman",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-03-30 00:00:00",
"metadata": {
"meeting_date": "2025-03-30 00:00:00",
"duration_min": "23"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-03"
],
"year_month": "2025-03",
"meeting_title": "5536128 - Unitypeak Nonprofits - sales_pitch - Discussion with Fernando Chapman",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi Fernando\u2014Megan Foster here. Thanks for taking a few minutes today. I work with revenue teams that live in Salesforce or HubSpot but want a cleaner way to turn what they already know about accounts into the next best actions, and also tighten forecasting so quarter-end isn\u2019t a surprise. How are things going at Unitypeak Nonprofits?",
"link": "https://www.onyx.app/6638533-1"
},
{
"text": "Fernando Chapman: Hey Megan\u2014doing well, thanks. We\u2019re in the middle of planning for the next quarter, so honestly forecasting and pipeline health are top of mind.",
"link": "https://www.onyx.app/6638533-2"
},
{
"text": "Megan Foster: Perfect timing then. I\u2019ll keep this quick. The product is called Sales_Accelerator\u2014think of it as a sales execution layer that sits on top of your CRM. It doesn\u2019t replace Salesforce; it pulls in your existing stages plus activity signals\u2014emails, calls, meetings\u2014and then explains what\u2019s driving your forecast and flags deals that are drifting. The big value is: more accurate commit numbers and clear guidance on what to do next on each opportunity.",
"link": "https://www.onyx.app/6638533-3"
},
{
"text": "Fernando Chapman: We\u2019ve tried a couple forecasting add-ons before. They usually end up being \u201canother dashboard\u201d that reps don\u2019t use. What\u2019s different here?",
"link": "https://www.onyx.app/6638533-4"
},
{
"text": "Megan Foster: Totally fair. Two things that tend to change adoption. First, the forecast isn\u2019t just a number\u2014it\u2019s explainable. For each opportunity it shows drivers like stage age, last touch, whether a next meeting is scheduled, and historical win rate for similar deals. So a manager can say, \u201cThis is in commit, but it\u2019s 21 days stale with no next meeting\u2014why?\u201d \nSecond, it\u2019s action-oriented: reps get \u201cbest next action\u201d prompts tied to those drivers, like \u201cbook next meeting,\u201d \u201csend follow-up sequence,\u201d or \u201cre-engage champion,\u201d rather than passively looking at charts.",
"link": "https://www.onyx.app/6638533-5"
},
{
"text": "Fernando Chapman: Okay. How does it handle different segments? We have a mix\u2014smaller nonprofits with faster cycles, and larger orgs that move slow and have more stakeholders.",
"link": "https://www.onyx.app/6638533-6"
},
{
"text": "Megan Foster: That\u2019s exactly where it helps. Sales_Accelerator can forecast by segment, rep, and source, and it tracks stage conversion and slippage diagnostics. So you\u2019ll see, for example, \u201cEnterprise nonprofit segment is slipping in security review\u201d or \u201cMid-market is stuck between discovery and demo.\u201d Then sales ops can test a process fix\u2014like pulling a security checklist earlier\u2014and monitor whether conversion improves over the next few weeks.",
"link": "https://www.onyx.app/6638533-7"
},
{
"text": "Fernando Chapman: The bottleneck visibility is interesting. Right now we do that manually in spreadsheets, and it\u2019s\u2026 not fun. But reps will ask, \u201cWhere is this coming from?\u201d",
"link": "https://www.onyx.app/6638533-8"
},
{
"text": "Megan Foster: Yep\u2014and that\u2019s why the \u201cdrivers\u201d piece matters. It\u2019s not a black box. If the forecast changes, it will show why: stage age creeping up, fewer touches than your benchmark, no meeting on the calendar, or a lower historical win rate for deals that look like this one. That way your sales leaders can coach with evidence, and reps feel it\u2019s fair because it\u2019s based on the same activity and CRM data they already use.",
"link": "https://www.onyx.app/6638533-9"
},
{
"text": "Fernando Chapman: We\u2019re also trying to reduce end-of-quarter fire drills. Any tool claims it helps that.",
"link": "https://www.onyx.app/6638533-10"
},
{
"text": "Megan Foster: For sure. Where we typically see impact is early warning. You get alerts when deals are at risk\u2014like \u201cstalled in stage beyond benchmark,\u201d \u201clast touch exceeded threshold,\u201d or \u201cpushed close date twice.\u201d Instead of finding out in week 12, you see it in week 6 and can re-allocate time or build coverage. That translates into fewer surprises and a more realistic commit.",
"link": "https://www.onyx.app/6638533-11"
},
{
"text": "Fernando Chapman: Makes sense. We also care about accountability\u2014managers need to know what changed week to week.",
"link": "https://www.onyx.app/6638533-12"
},
{
"text": "Megan Foster: Exactly. Sales_Accelerator gives you commit / best-case / pipeline views, and then breakouts by rep and segment. On top of that, it\u2019s good for scenario planning. For example: \u201cIf we add 20 SQLs in our top segment, what\u2019s the expected impact on pipeline and commit?\u201d It uses your historical conversion and cycle-time benchmarks, so you can sanity-check growth plans with real data.",
"link": "https://www.onyx.app/6638533-13"
},
{
"text": "Fernando Chapman: Scenario planning would help. Our leadership always asks, \u201cWhat happens if we hire two more SDRs?\u201d and we end up guessing.",
"link": "https://www.onyx.app/6638533-14"
},
{
"text": "Megan Foster: Right\u2014and the nice part is it\u2019s grounded in your funnel reality, not generic benchmarks. Another quick example tied to your earlier point about segments: you can model \u201cIf we improve conversion from discovery to demo by 5% in enterprise nonprofits, what happens to forecasted revenue?\u201d That\u2019s a concrete way for sales ops to prioritize improvements.",
"link": "https://www.onyx.app/6638533-15"
},
{
"text": "Fernando Chapman: What does implementation look like? I don\u2019t want a six-month project.",
"link": "https://www.onyx.app/6638533-16"
},
{
"text": "Megan Foster: Totally. Typically it\u2019s lightweight because we\u2019re sitting on top of the CRM. We connect Salesforce or HubSpot, pull stages and historical opportunity data, and connect activity signals. Then we calibrate benchmarks\u2014cycle time by stage, touch thresholds, and basic segments. Many teams see value in a few weeks, not months, because the first win is visibility: what\u2019s stuck, what\u2019s slipping, and which deals need attention now.",
"link": "https://www.onyx.app/6638533-17"
},
{
"text": "Fernando Chapman: And it won\u2019t disrupt how reps work day to day?",
"link": "https://www.onyx.app/6638533-18"
},
{
"text": "Megan Foster: That\u2019s the goal\u2014minimal disruption. Reps still live in the CRM, but Sales_Accelerator surfaces prioritized actions and risks without making them re-enter data. Managers and ops get the forecasting and diagnostics layer that\u2019s usually missing in the CRM alone.",
"link": "https://www.onyx.app/6638533-19"
},
{
"text": "Fernando Chapman: Okay. This is definitely in our problem area. What would you need from us to see if this fits?",
"link": "https://www.onyx.app/6638533-20"
},
{
"text": "Megan Foster: Great question. For a proper evaluation, I\u2019d suggest a 30\u201345 minute working session with you and whoever owns forecasting\u2014maybe sales ops or a frontline manager. We\u2019ll map your stages, define two or three key segments, and I\u2019ll show you how the drivers and slippage diagnostics would look using a sample view. If you\u2019re open to it, we can also identify one bottleneck you suspect\u2014like late-stage approvals\u2014and walk through how we\u2019d test a process fix and monitor improvement.",
"link": "https://www.onyx.app/6638533-21"
},
{
"text": "Fernando Chapman: That sounds reasonable. I can loop in our sales ops manager\u2014she\u2019s been pushing for better pipeline diagnostics.",
"link": "https://www.onyx.app/6638533-22"
},
{
"text": "Megan Foster: Perfect. How about we schedule that for early next week? I can send an agenda: forecasting drivers, stage conversion/slippage by segment and rep, and a quick look at scenario planning. Then we\u2019ll confirm success criteria\u2014like reducing stale opportunities in commit and improving forecast accuracy.",
"link": "https://www.onyx.app/6638533-23"
},
{
"text": "Fernando Chapman: Monday or Tuesday afternoon could work. Send me a couple options and I\u2019ll bring her in.",
"link": "https://www.onyx.app/6638533-24"
},
{
"text": "Megan Foster: Will do. Last quick check: are you on Salesforce today, and do you track calls/emails through a tool like Outreach or directly in the CRM?",
"link": "https://www.onyx.app/6638533-25"
},
{
"text": "Fernando Chapman: We\u2019re on Salesforce. Activity is a mix\u2014some logged in Salesforce, some through Outreach.",
"link": "https://www.onyx.app/6638533-26"
},
{
"text": "Megan Foster: Great\u2014that\u2019s common and workable. I\u2019ll email two time slots and a short prep list\u2014nothing heavy. Appreciate the time, Fernando. Looking forward to digging in with you and your ops lead.",
"link": "https://www.onyx.app/6638533-27"
},
{
"text": "Fernando Chapman: Thanks Megan. This sounds promising\u2014send it over and we\u2019ll get it on the calendar.",
"link": "https://www.onyx.app/6638533-28"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "fernando_chapman@unitypeak_nonprofits.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_7435001",
"semantic_identifier": "7435001 - Keystone Atlas Partners - sales_pitch - Discussion with Mason Davis",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-03-15 00:00:00",
"metadata": {
"meeting_date": "2025-03-15 00:00:00",
"duration_min": "37"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-03"
],
"year_month": "2025-03",
"meeting_title": "7435001 - Keystone Atlas Partners - sales_pitch - Discussion with Mason Davis",
"organizer_email": "irene_shen@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Irene Shen: Hi Mason\u2014this is Irene Shen. Thanks for taking a few minutes. I\u2019ll keep it brief: I\u2019m calling because we\u2019ve been helping AEs prevent deals from stalling by turning CRM activity into very specific \u201cwhat to do next\u201d guidance, and I thought it could map well to how Keystone Atlas Partners runs pipeline.",
"link": "https://www.onyx.app/6235252-1"
},
{
"text": "Mason Davis: Hey Irene. Sure, I\u2019ve got a few minutes. What exactly are you selling?",
"link": "https://www.onyx.app/6235252-2"
},
{
"text": "Irene Shen: The product is called Sales_Accelerator. It sits on top of Salesforce or HubSpot\u2014so it\u2019s not another CRM\u2014and it focuses on deal execution. The core is: it forecasts outcomes, flags risk early, and, most importantly for reps, it recommends the next best follow-up on each opportunity based on what\u2019s actually happened and who\u2019s involved.",
"link": "https://www.onyx.app/6235252-3"
},
{
"text": "Mason Davis: We already have forecasting and dashboards. What\u2019s different about it?",
"link": "https://www.onyx.app/6235252-4"
},
{
"text": "Irene Shen: Totally fair. Most dashboards tell you what happened. Sales_Accelerator tells a rep what to do next\u2014inside the CRM\u2014so deals keep moving. For example, one feature we lead with is sales follow-up recommendations: it looks at stage, stakeholder coverage, whether there\u2019s a mutual action plan, and recent activity. Then it suggests the next step and can draft the actual follow-up, like a recap email with next steps and due dates.",
"link": "https://www.onyx.app/6235252-5"
},
{
"text": "Mason Davis: Drafting emails sounds like another AI writing tool. We\u2019ve tried those and adoption wasn\u2019t great.",
"link": "https://www.onyx.app/6235252-6"
},
{
"text": "Irene Shen: Yeah, generic \u201cAI email writer\u201d usually fails because it\u2019s not connected to the deal context or your approved messaging. The difference here is the recommendation is triggered by real deal signals\u2014like, \u201cmeeting happened, but no follow-up was logged,\u201d or \u201conly one contact engaged for three weeks,\u201d or \u201cmutual plan incomplete for this stage.\u201d And the content it drafts is anchored to your positioning, not random. If you already use something like Customer_Analyzer for fit scores and positioning blocks, Sales_Accelerator consumes that\u2014so reps get tailored proof points and objection responses that are consistent with what you\u2019ve approved.",
"link": "https://www.onyx.app/6235252-7"
},
{
"text": "Mason Davis: We don\u2019t use Customer_Analyzer today. Does that matter?",
"link": "https://www.onyx.app/6235252-8"
},
{
"text": "Irene Shen: Not required. Sales_Accelerator still works off your CRM data and activity signals. Customer_Analyzer just strengthens the tailoring layer if you have it\u2014fit scoring, positioning blocks, that kind of thing. But even without it, the immediate value is: fewer stalls, tighter follow-up, and more consistent progression across reps without forcing a rigid methodology.",
"link": "https://www.onyx.app/6235252-9"
},
{
"text": "Mason Davis: Okay. Tell me what \u201crecommend the next action\u201d looks like day-to-day. Reps don\u2019t want another place to check.",
"link": "https://www.onyx.app/6235252-10"
},
{
"text": "Irene Shen: Exactly\u2014so we don\u2019t make them. It shows up as a lightweight task queue right in the opportunity view or via a browser extension. Think of it like: open an opp, and you see \u201cNext best action: send recap + confirm stakeholders + propose mutual action plan.\u201d One click generates the recap email, one click creates the mutual action plan checklist with reminders, and it nudges multi-threading when there\u2019s only one active contact.",
"link": "https://www.onyx.app/6235252-11"
},
{
"text": "Mason Davis: Multi-threading is a big one for us\u2014deals die because we can\u2019t get to the economic buyer. How does your tool know that?",
"link": "https://www.onyx.app/6235252-12"
},
{
"text": "Irene Shen: We look at stakeholder engagement and activity patterns. If an opportunity has only one contact with recent engagement, or if key roles are missing for that stage\u2014like no finance or no exec sponsor\u2014we flag it and recommend a multi-threading action. That might include suggested outreach targets based on your account data, plus a sequence suggestion: email + LinkedIn touch + call cadence, spread over a few days, so it\u2019s actionable rather than just \u201cyou should multi-thread.\u201d",
"link": "https://www.onyx.app/6235252-13"
},
{
"text": "Mason Davis: And you said it can do a mutual action plan?",
"link": "https://www.onyx.app/6235252-14"
},
{
"text": "Irene Shen: Yes. After discovery, a super common stall is: everyone leaves the call feeling good, but there\u2019s no written plan, no dates, and no clear stakeholders. In our AE use case, Sales_Accelerator drafts the discovery recap email, inserts two tailored proof points aligned to the account context, and then generates a mutual action plan checklist\u2014things like \u201cSecurity review,\u201d \u201cROI validation,\u201d \u201cStakeholder meeting,\u201d with proposed due dates. It also reminds the rep if those items aren\u2019t moving, so the deal doesn\u2019t silently slip.",
"link": "https://www.onyx.app/6235252-15"
},
{
"text": "Mason Davis: Our managers will ask: does it actually improve forecasting accuracy, or is it just coaching?",
"link": "https://www.onyx.app/6235252-16"
},
{
"text": "Irene Shen: It\u2019s both, but the reason forecasting improves is because we measure concrete progress signals\u2014not just stage. When mutual plans aren\u2019t complete, or activity drops, or stakeholder coverage is thin, those are early warning signals. Managers get a view of at-risk deals with the specific reason and the recommended intervention\u2014so it\u2019s not a vague \u201cred/yellow/green.\u201d It\u2019s \u201cNo follow-up after last meeting\u201d or \u201cSingle-threaded, no exec contact,\u201d and here\u2019s the best next step.",
"link": "https://www.onyx.app/6235252-17"
},
{
"text": "Mason Davis: I like the specificity. My concern is adoption\u2014reps hate \u201cprocess.\u201d",
"link": "https://www.onyx.app/6235252-18"
},
{
"text": "Irene Shen: Completely. Our approach is: don\u2019t add fields, don\u2019t force reps to learn a new methodology. We standardize the high-performing behaviors by making the next action the easiest action. When the tool auto-suggests the recap and next steps right after a meeting, reps save time and look more professional. Adoption usually comes from that immediate payoff, not from compliance.",
"link": "https://www.onyx.app/6235252-19"
},
{
"text": "Mason Davis: How heavy is the implementation? We\u2019re not looking for a big systems project.",
"link": "https://www.onyx.app/6235252-20"
},
{
"text": "Irene Shen: It\u2019s designed to be light. Since it sits on top of Salesforce/HubSpot, we connect to your existing objects and activity data. We typically start with a narrow scope\u2014one team or one pipeline segment\u2014and focus on the follow-up recommendations first. That way you can see if it reduces stalls and shortens cycle time before expanding.",
"link": "https://www.onyx.app/6235252-21"
},
{
"text": "Mason Davis: What would you need from us to evaluate it?",
"link": "https://www.onyx.app/6235252-22"
},
{
"text": "Irene Shen: For an initial evaluation, I\u2019d suggest a 30-minute working session with you and whoever owns sales ops or enablement\u2014just to map your stages, what \u201cgood\u201d looks like per stage, and where deals tend to get stuck. Then we can show you a demo using your terminology\u2014recap email, mutual action plan, multi-threading prompts\u2014so it\u2019s not theoretical.",
"link": "https://www.onyx.app/6235252-23"
},
{
"text": "Mason Davis: I can loop in our sales ops lead. But I want to make sure this isn\u2019t just going to spam my team with alerts.",
"link": "https://www.onyx.app/6235252-24"
},
{
"text": "Irene Shen: Great point. The goal isn\u2019t alert fatigue. Recommendations are prioritized and contextual\u2014only surfaced when there\u2019s a meaningful gap, like \u201cmeeting occurred without follow-up,\u201d \u201cno next meeting scheduled,\u201d \u201cstakeholder coverage incomplete,\u201d or \u201cmutual plan overdue.\u201d And you can tune thresholds by stage so it matches your motion.",
"link": "https://www.onyx.app/6235252-25"
},
{
"text": "Mason Davis: Got it. If it can help with follow-up discipline without being overbearing, that would be valuable. We\u2019ve had a few late-stage deals drift recently.",
"link": "https://www.onyx.app/6235252-26"
},
{
"text": "Irene Shen: That\u2019s exactly where this helps\u2014late-stage drift is usually visible in the signals before it hits the forecast. If you\u2019re open to it, let\u2019s schedule that deeper session. I can bring a quick demo focused on one flow: post-discovery follow-up\u2014recap email, proof points, mutual action plan, and the multi-threading recommendation when stakeholder coverage is thin.",
"link": "https://www.onyx.app/6235252-27"
},
{
"text": "Mason Davis: Yeah, let\u2019s do it. Next week would be best. Tuesday or Wednesday afternoon?",
"link": "https://www.onyx.app/6235252-28"
},
{
"text": "Irene Shen: Wednesday afternoon works. How about 2:00 PM your time for 30 minutes? And if you can invite your sales ops lead, we\u2019ll make it practical\u2014stage mapping, example recommendations, and what tuning would look like.",
"link": "https://www.onyx.app/6235252-29"
},
{
"text": "Mason Davis: 2:00 PM Wednesday is good. I\u2019ll add our sales ops lead to the invite.",
"link": "https://www.onyx.app/6235252-30"
},
{
"text": "Irene Shen: Perfect. I\u2019ll send a calendar hold and a short agenda. Thanks, Mason\u2014looking forward to showing you how Sales_Accelerator keeps deals from stalling without adding overhead.",
"link": "https://www.onyx.app/6235252-31"
},
{
"text": "Mason Davis: Sounds good. Talk next week, Irene.",
"link": "https://www.onyx.app/6235252-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "irene_shen@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "mason_davis@keystone_atlas_partners.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_8444416",
"semantic_identifier": "8444416 - Ironspruce Robotics - sales_pitch - Discussion with Nora Wheeler",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-03-08 00:00:00",
"metadata": {
"meeting_date": "2025-03-08 00:00:00",
"duration_min": "33"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-03"
],
"year_month": "2025-03",
"meeting_title": "8444416 - Ironspruce Robotics - sales_pitch - Discussion with Nora Wheeler",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi Nora, it\u2019s Megan Foster. Thanks for taking a few minutes\u2014did I catch you at an okay time?",
"link": "https://www.onyx.app/6682614-1"
},
{
"text": "Nora Wheeler: Hi Megan\u2014yes, I\u2019ve got about five minutes. What can I do for you?",
"link": "https://www.onyx.app/6682614-2"
},
{
"text": "Megan Foster: Perfect. I\u2019ll keep it tight. I\u2019m reaching out because a lot of product and go-to-market teams I talk to are trying to answer the same question you probably face at Ironspruce Robotics: \u201cWhich customer segments should we double down on next quarter, and how do we talk about the product in a way that actually lands?\u201d We built a SaaS product called Customer_Analyzer that helps teams get aligned on their best-fit customers and turns that into usable positioning for sales and marketing.",
"link": "https://www.onyx.app/6682614-3"
},
{
"text": "Nora Wheeler: Okay. We\u2019re definitely thinking about industry focus for next quarter. But we already have a CRM and dashboards. What\u2019s different here?",
"link": "https://www.onyx.app/6682614-4"
},
{
"text": "Megan Foster: Great question. Most dashboards tell you what happened. Customer_Analyzer is built to connect your customer signals\u2014things like CRM fields, onboarding surveys, product usage events, support tickets, even interview notes\u2014and translate them into two outcomes you can act on: a fit score by segment, and then positioning recommendations for the segments you want to prioritize.",
"link": "https://www.onyx.app/6682614-5"
},
{
"text": "Nora Wheeler: Fit score by segment\u2014so like a way to rank industries?",
"link": "https://www.onyx.app/6682614-6"
},
{
"text": "Megan Foster: Exactly. And not just by revenue. For example, one common use case is prioritizing which industries to target next quarter using retention and support burden signals. So you\u2019re not just chasing the loudest segment\u2014you\u2019re weighting who sticks around, who expands, and who doesn\u2019t drain your team with constant tickets.",
"link": "https://www.onyx.app/6682614-7"
},
{
"text": "Nora Wheeler: That\u2019s interesting. We\u2019ve had some segments that look great in bookings but turn into a support sinkhole.",
"link": "https://www.onyx.app/6682614-8"
},
{
"text": "Megan Foster: That\u2019s the exact pattern we see. The way teams use us is: they pull in renewal/retention data and support volume, tag it by industry or segment, and Customer_Analyzer surfaces \u201cthese are your highest-fit segments based on the drivers that correlate with healthy customers.\u201d Then the second part\u2014where teams usually get stuck\u2014is: \u201cCool, so what do we say to those segments?\u201d That\u2019s where our positioning_recommendation feature comes in.",
"link": "https://www.onyx.app/6682614-9"
},
{
"text": "Nora Wheeler: What does that actually produce? Because \u201cpositioning\u201d can get pretty fluffy.",
"link": "https://www.onyx.app/6682614-10"
},
{
"text": "Megan Foster: Totally fair. Ours is intentionally not a strategy deck. positioning_recommendation generates segment-specific messaging blocks that your team can immediately use and edit\u2014like website hero copy, sales deck bullets, and email openers. And it links each recommendation back to the evidence and drivers it used\u2014so you can see, \u201cWe\u2019re emphasizing fast deployment because usage events and onboarding survey language show that\u2019s what best-fit customers care about,\u201d for example.",
"link": "https://www.onyx.app/6682614-11"
},
{
"text": "Nora Wheeler: So it\u2019s like it\u2019s pulling language from what customers actually say?",
"link": "https://www.onyx.app/6682614-12"
},
{
"text": "Megan Foster: Exactly. It combines observed customer language\u2014support tickets, call notes, surveys\u2014with the fit drivers you\u2019re using to prioritize segments. Then it outputs: a value proposition for that segment, top use cases, key differentiators, proof points you should collect, some pricing/value framing notes, and an objection-handling playbook. The goal is consistency\u2014so marketing and sales aren\u2019t improvising different narratives for the same segment.",
"link": "https://www.onyx.app/6682614-13"
},
{
"text": "Nora Wheeler: The objection-handling part is compelling. Our reps struggle with the same two objections in a couple verticals and everyone answers differently.",
"link": "https://www.onyx.app/6682614-14"
},
{
"text": "Megan Foster: That\u2019s a really common pain. And because it\u2019s tied to the segment, you\u2019re not getting generic rebuttals. You\u2019re getting \u201cIn robotics manufacturing accounts, the top objection is X; here\u2019s the response, here are the proof points to gather, and here\u2019s the email line that tends to work.\u201d Then your team can tweak it to match your voice.",
"link": "https://www.onyx.app/6682614-15"
},
{
"text": "Nora Wheeler: How hard is it to set up? Because I don\u2019t have appetite for a big integration project right now.",
"link": "https://www.onyx.app/6682614-16"
},
{
"text": "Megan Foster: It\u2019s typically lightweight to start. For a first pass, teams often begin with a CRM export plus a support ticket export, then optionally add onboarding survey data or a small set of interview notes. You can get an initial segment ranking and draft positioning pretty quickly, then decide if you want to connect product usage events later for more signal.",
"link": "https://www.onyx.app/6682614-17"
},
{
"text": "Nora Wheeler: Got it. And who usually owns this internally\u2014product marketing? Sales ops?",
"link": "https://www.onyx.app/6682614-18"
},
{
"text": "Megan Foster: Usually product marketing or growth owns the positioning output, and RevOps or analytics helps with the inputs. But the point is to get a shared artifact everyone can align around: \u201cThese are our best-fit segments next quarter, and here\u2019s the messaging we\u2019re using.\u201d",
"link": "https://www.onyx.app/6682614-19"
},
{
"text": "Nora Wheeler: This is relevant. We\u2019re in planning right now and there\u2019s a debate between two industries. One has higher ACV, the other renews better and is way easier on support.",
"link": "https://www.onyx.app/6682614-20"
},
{
"text": "Megan Foster: That\u2019s a perfect scenario for us. In a full walkthrough, we\u2019d show you how to compare those two segments side by side: retention trend, support burden, and any other drivers you care about\u2014then generate positioning for the one you choose to prioritize so your campaigns and outbound are aligned from day one.",
"link": "https://www.onyx.app/6682614-21"
},
{
"text": "Nora Wheeler: Okay. What would you need from us to make that discussion concrete?",
"link": "https://www.onyx.app/6682614-22"
},
{
"text": "Megan Foster: For the full call, ideally: a simple breakdown of your customer list with industry tags, retention or renewal outcomes if you have them, and a support ticket volume metric by account\u2014nothing fancy. Even if it\u2019s imperfect, it\u2019s enough to show how the fit drivers work and what the positioning output looks like.",
"link": "https://www.onyx.app/6682614-23"
},
{
"text": "Nora Wheeler: That sounds doable. Can we include someone from product marketing on our side?",
"link": "https://www.onyx.app/6682614-24"
},
{
"text": "Megan Foster: Absolutely\u2014actually that\u2019s ideal. How about we schedule a 30-minute session where I demo Customer_Analyzer specifically around \u201cwhich industries to target next quarter\u201d and then show the positioning_recommendation outputs\u2014hero copy, sales bullets, and the objection-handling playbook\u2014for your top two segments?",
"link": "https://www.onyx.app/6682614-25"
},
{
"text": "Nora Wheeler: Yes, that would be useful. Next week is better than this week for me.",
"link": "https://www.onyx.app/6682614-26"
},
{
"text": "Megan Foster: Great. I have Tuesday at 11:00am or Thursday at 2:30pm\u2014either work?",
"link": "https://www.onyx.app/6682614-27"
},
{
"text": "Nora Wheeler: Thursday at 2:30 works. I\u2019ll invite our product marketing lead.",
"link": "https://www.onyx.app/6682614-28"
},
{
"text": "Megan Foster: Perfect. I\u2019ll send a calendar invite for Thursday at 2:30 with a short list of suggested inputs\u2014CRM export fields and a support ticket summary\u2014so we can make it relevant to Ironspruce Robotics. Sound good?",
"link": "https://www.onyx.app/6682614-29"
},
{
"text": "Nora Wheeler: Sounds good. Send it over and I\u2019ll take a look.",
"link": "https://www.onyx.app/6682614-30"
},
{
"text": "Megan Foster: Will do. Thanks, Nora\u2014appreciate the time, and looking forward to digging into those segments with you next Thursday.",
"link": "https://www.onyx.app/6682614-31"
},
{
"text": "Nora Wheeler: Thanks, Megan. Talk then.",
"link": "https://www.onyx.app/6682614-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "nora_wheeler@ironspruce_robotics.onyx.app"
}
]
}

View File

@@ -0,0 +1,153 @@
{
"id": "FIREFLIES_9151429",
"semantic_identifier": "9151429 - Blueharbor Dynamics - sales_pitch - Discussion with Nikhil Verma",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-03-21 00:00:00",
"metadata": {
"meeting_date": "2025-03-21 00:00:00",
"duration_min": "45"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-03"
],
"year_month": "2025-03",
"meeting_title": "9151429 - Blueharbor Dynamics - sales_pitch - Discussion with Nikhil Verma",
"organizer_email": "camila_vega@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Camila Vega: Hi Nikhil\u2014Camila Vega here. Thanks for taking a few minutes. I\u2019ll keep this quick: I\u2019m calling because we built Customer_Analyzer to help product and go-to-market teams get crisp on who their best customers are and what to build and say to win more of them. Curious\u2014are you currently trying to tighten your ICP or make roadmap decisions based on which segments are actually healthiest?",
"link": "https://www.onyx.app/7262916-1"
},
{
"text": "Nikhil Verma: Hey Camila, yeah, that\u2019s definitely on our list. We\u2019ve been debating which segment to lean into this year, and roadmap requests are all over the place.",
"link": "https://www.onyx.app/7262916-2"
},
{
"text": "Camila Vega: That\u2019s exactly the scenario Customer_Analyzer is designed for. The core piece I wanted to highlight is our fit estimation: we ingest signals you already have\u2014CRM fields, onboarding survey responses, product usage events, support tickets, even interview notes\u2014and we output a fit score from 0 to 100 at the account and segment level. Then we show the driver breakdown so you can see why a segment scores high or low, like adoption patterns, support burden, or time-to-value.",
"link": "https://www.onyx.app/7262916-3"
},
{
"text": "Nikhil Verma: We do have a lot of that data, but it\u2019s scattered. We\u2019ve tried building dashboards, and it turns into a data project that never ends.",
"link": "https://www.onyx.app/7262916-4"
},
{
"text": "Camila Vega: Totally\u2014dashboards tell you what happened, but they don\u2019t usually answer \u201cwho should we bet on\u201d or \u201cwhat\u2019s the best segment for the next two quarters.\u201d With fit estimation, you can compare segments directly\u2014like SMB versus mid-market\u2014and see not just revenue, but who retains, expands, and adopts quickly. Then it recommends an ICP profile based on observed outcomes, not just gut feel.",
"link": "https://www.onyx.app/7262916-5"
},
{
"text": "Nikhil Verma: When you say \u201crecommended ICP,\u201d what does that actually look like? Like a persona?",
"link": "https://www.onyx.app/7262916-6"
},
{
"text": "Camila Vega: It\u2019s more actionable than a persona doc. You get an ICP profile defined by the fit drivers you choose\u2014industry, size, technographics, severity of the problem you solve, willingness to pay, adoption/usage patterns, churn risk signals\u2014and it\u2019s tied to what your best customers actually do in product and in support. So instead of \u201cwe think mid-market fintech is good,\u201d you\u2019ll see \u201caccounts with X stack + Y onboarding behavior + low support burden + fast time-to-value are consistently high fit,\u201d and you can quantify it.",
"link": "https://www.onyx.app/7262916-7"
},
{
"text": "Nikhil Verma: That would help. Our biggest issue is roadmap planning\u2014everyone brings anecdotes from their favorite customers.",
"link": "https://www.onyx.app/7262916-8"
},
{
"text": "Camila Vega: Perfect use case. For product planning, teams use Customer_Analyzer to identify high-fit customers with unmet needs\u2014so you can separate \u201cloud requests\u201d from \u201crequests coming from customers who look like your best long-term cohort.\u201d It\u2019s a way to make roadmap bets with evidence: focus on problems that show up in high-fit segments, not just whoever is shouting the most.",
"link": "https://www.onyx.app/7262916-9"
},
{
"text": "Nikhil Verma: How do you handle situations where usage looks good but support is a mess? We\u2019ve got a segment like that\u2014active users, but tons of tickets.",
"link": "https://www.onyx.app/7262916-10"
},
{
"text": "Camila Vega: That\u2019s where the driver breakdown is really helpful. Support burden is one of the fit drivers we see matter a lot. You might find the fit score is dragged down by ticket volume or specific ticket themes, even if usage is high. And because it\u2019s at both account and segment level, you can see whether it\u2019s a few outliers or a segment pattern\u2014then decide if it\u2019s a product investment or a segment you should deprioritize.",
"link": "https://www.onyx.app/7262916-11"
},
{
"text": "Nikhil Verma: Interesting. We\u2019ve argued about that exact thing\u2014whether it\u2019s \u201cbad customers\u201d or \u201cour product is missing something.\u201d",
"link": "https://www.onyx.app/7262916-12"
},
{
"text": "Camila Vega: Exactly. The point isn\u2019t to label customers as \u201cbad\u201d\u2014it\u2019s to make the tradeoffs explicit. If a segment is high revenue but consistently slow time-to-value and high support burden, the model will surface that. Then you can decide: invest to improve onboarding and reduce tickets, or focus acquisition on the segment that\u2019s already succeeding with your current strengths.",
"link": "https://www.onyx.app/7262916-13"
},
{
"text": "Nikhil Verma: What does implementation look like? I\u2019m wary of anything that takes months.",
"link": "https://www.onyx.app/7262916-14"
},
{
"text": "Camila Vega: Fair. The quick version: we start with the data you already have. Most teams connect CRM plus product events and at least one qualitative source\u2014like onboarding surveys or support tickets. Then we work with you to define a handful of fit drivers that reflect your reality. You don\u2019t need 50 fields; you need the 6\u201310 signals that correlate with retention, expansion, and smooth adoption. From there, you\u2019ll get initial segment comparisons and a first pass ICP recommendation pretty quickly, and you can iterate.",
"link": "https://www.onyx.app/7262916-15"
},
{
"text": "Nikhil Verma: Who typically owns it? Product ops? Rev ops?",
"link": "https://www.onyx.app/7262916-16"
},
{
"text": "Camila Vega: Usually a product ops or growth/product analytics lead partners with rev ops on the CRM side. But it\u2019s meant to be shared\u2014product uses it for roadmap planning, marketing uses it for targeting and messaging, and sales uses it for qualification. The fit score creates a common language across teams.",
"link": "https://www.onyx.app/7262916-17"
},
{
"text": "Nikhil Verma: We\u2019ve struggled with alignment, so that\u2019s appealing. But I\u2019d need to see how \u201cfit\u201d is calculated. We\u2019ve been burned by black-box scoring before.",
"link": "https://www.onyx.app/7262916-18"
},
{
"text": "Camila Vega: Completely reasonable. It\u2019s not a black box\u2014you select or define the drivers, and the output includes the breakdown: \u201chere\u2019s the score, and here\u2019s what contributed.\u201d If churn risk signals are pulling a segment down, you\u2019ll see it. If time-to-value is boosting another segment, you\u2019ll see that. The whole idea is transparency so the team trusts the result.",
"link": "https://www.onyx.app/7262916-19"
},
{
"text": "Nikhil Verma: Okay. If we did this, what would you recommend as a first question to answer?",
"link": "https://www.onyx.app/7262916-20"
},
{
"text": "Camila Vega: For Blueharbor Dynamics, I\u2019d start with: \u201cWhich segment should we prioritize for the next two quarters given our current product strengths?\u201d Then we\u2019d compare your current segments across fit score and key drivers\u2014adoption patterns, support burden, and time-to-value are typically the fastest to reveal differences. From that, product planning becomes less about anecdotes and more about measurable cohorts.",
"link": "https://www.onyx.app/7262916-21"
},
{
"text": "Nikhil Verma: That\u2019s basically the question we\u2019re trying to answer right now.",
"link": "https://www.onyx.app/7262916-22"
},
{
"text": "Camila Vega: Great. What I\u2019d suggest is a longer working session\u2014maybe 30 minutes\u2014where you and I map your current segments and the signals you already track, and I can show you what the fit score output and driver breakdown looks like in practice. Then we can decide if it\u2019s worth a deeper evaluation. Would next week work?",
"link": "https://www.onyx.app/7262916-23"
},
{
"text": "Nikhil Verma: Yeah, I\u2019m open to that. Early next week is best.",
"link": "https://www.onyx.app/7262916-24"
},
{
"text": "Camila Vega: Perfect\u2014how\u2019s Tuesday at 10:30am your time? If that\u2019s tight, I can do Monday afternoon as well.",
"link": "https://www.onyx.app/7262916-25"
},
{
"text": "Nikhil Verma: Tuesday at 10:30 works. Send an invite and include what you need from us beforehand.",
"link": "https://www.onyx.app/7262916-26"
},
{
"text": "Camila Vega: Will do. I\u2019ll send a calendar invite for Tuesday 10:30 with a short checklist\u2014mostly what systems you use for CRM and product events, and what segments you\u2019re debating. Thanks, Nikhil\u2014looking forward to it.",
"link": "https://www.onyx.app/7262916-27"
},
{
"text": "Nikhil Verma: Sounds good. Thanks, Camila.",
"link": "https://www.onyx.app/7262916-28"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "camila_vega@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "nikhil_verma@blueharbor_dynamics.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_1054066",
"semantic_identifier": "1054066 - Jadecircuit Electronics - sales_call - Discussion with Liliana Zamora",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-04-09 00:00:00",
"metadata": {
"meeting_date": "2025-04-09 00:00:00",
"duration_min": "43"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-04"
],
"year_month": "2025-04",
"meeting_title": "1054066 - Jadecircuit Electronics - sales_call - Discussion with Liliana Zamora",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hi Liliana\u2014good to see you again. Last time you mentioned you\u2019re trying to prioritize which industries to target next quarter, but retention and support burden are pulling you in different directions. Does that still capture the main goal for today?",
"link": "https://www.onyx.app/1969745-1"
},
{
"text": "Liliana Zamora: Hi James, yes. We have a few industries that look promising on pipeline, but some of them are a headache for support. Leadership wants a recommendation we can stand behind.",
"link": "https://www.onyx.app/1969745-2"
},
{
"text": "James Choi: Perfect. The way Customer_Analyzer helps here is we turn those signals\u2014retention, expansion, support tickets, even usage\u2014into a clear segment view, and then we generate positioning recommendations so your team can actually act on it. For today, I\u2019d like to focus on two things: how the positioning recommendation feature works, and how you\u2019d use it to pick industries for next quarter. Sound good?",
"link": "https://www.onyx.app/1969745-3"
},
{
"text": "Liliana Zamora: Yes, that\u2019s what I\u2019m interested in. I\u2019m especially curious how it turns that into something marketing and sales can use.",
"link": "https://www.onyx.app/1969745-4"
},
{
"text": "James Choi: Great. So once we ingest the signals, we identify the segments that are performing well\u2014say \u201cmid-market industrial OEMs\u201d versus \u201csmall contract manufacturers,\u201d whatever your taxonomy is. Then positioning_recommendation outputs editable messaging blocks tied to the fit drivers and evidence. For example: a segment-specific value proposition, top use cases, differentiators, proof points to collect, and an objection-handling playbook. It\u2019s not just \u201chere\u2019s a persona\u201d\u2014it\u2019s \u201chere\u2019s what to say, backed by why.\u201d",
"link": "https://www.onyx.app/1969745-5"
},
{
"text": "Liliana Zamora: Okay. When you say \u201cbacked by why,\u201d what does that look like? We\u2019ve had tools spit out generic messaging that doesn\u2019t match reality.",
"link": "https://www.onyx.app/1969745-6"
},
{
"text": "James Choi: Totally fair. In Customer_Analyzer, every recommended message links back to the drivers and signals that produced it. So if the value prop says, \u201cReduce downtime through faster part traceability,\u201d you can click into the underlying evidence\u2014like higher retention among accounts using traceability features weekly, or support tickets showing that segments who care about compliance are more satisfied when onboarding includes specific workflows. The idea is your team can sanity-check it, and edit it with confidence.",
"link": "https://www.onyx.app/1969745-7"
},
{
"text": "Liliana Zamora: That\u2019s helpful. One concern I have is our data quality. Our CRM fields are incomplete, and our support tickets aren\u2019t consistently tagged. Won\u2019t that mess up the outputs?",
"link": "https://www.onyx.app/1969745-8"
},
{
"text": "James Choi: It can, and I\u2019m glad you\u2019re raising it now. Data quality and missing fields do reduce scoring accuracy if we treat everything as equally reliable. What we typically do in week one is a data audit: we map what fields you have, assess coverage, and then pick a \u201cminimum viable\u201d set of signals that are consistently available\u2014often retention, plan tier, industry, and a couple of usage events. We can still generate strong positioning recommendations from a smaller, clean set, and then improve fidelity as you standardize tagging over time.",
"link": "https://www.onyx.app/1969745-9"
},
{
"text": "Liliana Zamora: So you\u2019re not saying we need to fix our whole CRM before we can use this.",
"link": "https://www.onyx.app/1969745-10"
},
{
"text": "James Choi: Exactly. We don\u2019t want that to become a blocker. For your use case\u2014prioritizing industries next quarter\u2014we can start with retention and support burden, plus whatever usage events you trust most. Then the recommendations will explicitly show confidence levels and what evidence is missing. That way, if an industry looks attractive but the support burden data is messy, it\u2019s visible rather than hidden.",
"link": "https://www.onyx.app/1969745-11"
},
{
"text": "Liliana Zamora: I like the transparency angle. Another question: our marketing team needs actual copy\u2014website hero statements, email openers, sales deck bullets. Does the product really give those, or is it more like guidance?",
"link": "https://www.onyx.app/1969745-12"
},
{
"text": "James Choi: It provides the actual blocks, and they\u2019re editable. For a prioritized segment, you\u2019d see something like: website hero copy options, \u201cthree bullets for a sales deck,\u201d a short outbound email opener, and then the proof points to collect\u2014like \u201ccapture a case study emphasizing reduced rework\u201d or \u201cquantify support response time improvements.\u201d And importantly, it\u2019s consistent across channels so sales and marketing aren\u2019t telling different stories.",
"link": "https://www.onyx.app/1969745-13"
},
{
"text": "Liliana Zamora: That alignment is a big deal for us. Sales tends to improvise based on what they think will land.",
"link": "https://www.onyx.app/1969745-14"
},
{
"text": "James Choi: Exactly\u2014and that\u2019s where you see longer cycles and more churn. If you pick an industry to target, Customer_Analyzer helps ensure the narrative matches what your best customers actually value, and arms sales with an objection-handling playbook. For example, if one industry consistently pushes back on implementation effort, the playbook would recommend the best counterpoints and what proof to cite.",
"link": "https://www.onyx.app/1969745-15"
},
{
"text": "Liliana Zamora: Okay, but how do you decide which industries are \u201cbest\u201d? We have some customers who renew but are unprofitable because support is intense.",
"link": "https://www.onyx.app/1969745-16"
},
{
"text": "James Choi: Great nuance. We don\u2019t just look at retention in isolation. For industry prioritization, we can include support burden as a negative signal\u2014ticket volume, severity, time-to-resolution. So an industry with decent retention but very high support cost can score lower than an industry with slightly lower retention but clean support patterns and stronger product usage depth. The output you get is a clear ranking with the drivers spelled out.",
"link": "https://www.onyx.app/1969745-17"
},
{
"text": "Liliana Zamora: That would be useful. Right now we\u2019re doing this in spreadsheets and arguing about it.",
"link": "https://www.onyx.app/1969745-18"
},
{
"text": "James Choi: Exactly the pain we\u2019re trying to remove. Let me make this concrete. Suppose next quarter you\u2019re choosing between Industry A and Industry B. Customer_Analyzer would show: \u201cIndustry A retains 12% better but generates 2.3x tickets; Industry B retains slightly less but has higher feature adoption tied to expansion.\u201d Then the positioning recommendation for the chosen industry would say, \u201cLead with X use case, differentiate on Y, collect Z proof.\u201d It\u2019s a decision plus an execution kit.",
"link": "https://www.onyx.app/1969745-19"
},
{
"text": "Liliana Zamora: That\u2019s the part I\u2019m trying to get to\u2014less debate, more action. What would you need from us to get a first pass of that?",
"link": "https://www.onyx.app/1969745-20"
},
{
"text": "James Choi: For a fast start, we\u2019d want: your current customer list with industry, retention/cohort data if you have it, and a support export\u2014even if tags are imperfect. Then 2\u20133 key product usage events you trust. We can ingest that and deliver an initial industry prioritization plus positioning blocks within about two weeks. And we\u2019d do a review session with you where you tell us what\u2019s off, what resonates, and we tune from there.",
"link": "https://www.onyx.app/1969745-21"
},
{
"text": "Liliana Zamora: Two weeks is reasonable. I\u2019m still slightly worried about inconsistent industry naming in the CRM\u2014some reps type it manually.",
"link": "https://www.onyx.app/1969745-22"
},
{
"text": "James Choi: That\u2019s common. We can normalize industries during onboarding\u2014mapping variants to a standard taxonomy. We can also start with broader buckets if needed and refine later. The key is: we don\u2019t want perfect to be the enemy of useful, especially since you\u2019re making a near-term targeting decision.",
"link": "https://www.onyx.app/1969745-23"
},
{
"text": "Liliana Zamora: Makes sense. If we do this, who typically owns the ongoing edits to the messaging blocks\u2014marketing or product marketing?",
"link": "https://www.onyx.app/1969745-24"
},
{
"text": "James Choi: Usually product marketing owns the final messaging, with sales leadership reviewing. The nice part is the blocks are editable, but still linked to drivers and evidence\u2014so when someone edits, they\u2019re not starting from scratch, and they\u2019re less likely to drift into \u201cgeneric claims.\u201d We can also set a cadence where you refresh recommendations quarterly as segments shift.",
"link": "https://www.onyx.app/1969745-25"
},
{
"text": "Liliana Zamora: Okay. This is sounding like what we need. What are the next steps on your side to get to a purchase decision?",
"link": "https://www.onyx.app/1969745-26"
},
{
"text": "James Choi: Given you\u2019ve already seen the concept, I\u2019d suggest we do a short working session: you bring a sample of customer data\u2014maybe 50\u2013100 accounts\u2014and we run a lightweight analysis to produce an example industry ranking and one segment\u2019s positioning package. If that resonates, we move straight into the annual subscription and onboarding plan. Would you be open to scheduling that for next week?",
"link": "https://www.onyx.app/1969745-27"
},
{
"text": "Liliana Zamora: Yes. I can pull a sample dataset and a support export. I\u2019ll need to loop in RevOps briefly for the CRM fields, but I can own it.",
"link": "https://www.onyx.app/1969745-28"
},
{
"text": "James Choi: Perfect. I\u2019ll send a secure upload link and a simple checklist of the fields that are \u201cnice to have\u201d versus \u201cmust have,\u201d so you\u2019re not chasing everything. After the working session, if you feel good, we can finalize the order and target kickoff the following week.",
"link": "https://www.onyx.app/1969745-29"
},
{
"text": "Liliana Zamora: That works. If you can also include some guidance on the minimum usage events, I\u2019ll ask our product analytics person for those.",
"link": "https://www.onyx.app/1969745-30"
},
{
"text": "James Choi: Absolutely. I\u2019ll propose two or three common ones\u2014like activation milestone, weekly active usage of a key workflow, and feature adoption tied to your value. You can confirm what\u2019s easiest. I\u2019ll email all that today and include a calendar invite for a 60-minute working session next Wednesday. Does morning or afternoon work better?",
"link": "https://www.onyx.app/1969745-31"
},
{
"text": "Liliana Zamora: Afternoon is better. Let\u2019s do 2:00.",
"link": "https://www.onyx.app/1969745-32"
},
{
"text": "James Choi: Done. I\u2019ll send it over. Thanks, Liliana\u2014this is a great fit for what you\u2019re trying to solve, and I\u2019m confident the sample output will make the decision much easier internally.",
"link": "https://www.onyx.app/1969745-33"
},
{
"text": "Liliana Zamora: Thanks, James. Looking forward to seeing the first pass.",
"link": "https://www.onyx.app/1969745-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "liliana_zamora@jadecircuit_electronics.onyx.app"
}
]
}

View File

@@ -0,0 +1,161 @@
{
"id": "FIREFLIES_2381286",
"semantic_identifier": "2381286 - Ultraviolet Summit Labs - sales_pitch - Discussion with Natalie Collins",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-04-15 00:00:00",
"metadata": {
"meeting_date": "2025-04-15 00:00:00",
"duration_min": "28"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-04"
],
"year_month": "2025-04",
"meeting_title": "2381286 - Ultraviolet Summit Labs - sales_pitch - Discussion with Natalie Collins",
"organizer_email": "mina_park@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Mina Park: Hi Natalie\u2014Mina Park here. Thanks for taking a few minutes. I\u2019ll keep this quick. I\u2019m calling because we built Customer_Analyzer to help product and go-to-market teams get crisp on who their best customers are and how to position to win more of them, using the signals you already have\u2014CRM fields, onboarding surveys, product usage, support tickets, even interview notes.",
"link": "https://www.onyx.app/9916329-1"
},
{
"text": "Natalie Collins: Hi Mina, sure. We\u2019re in between meetings, but I can do a quick overview. What are you seeing teams struggle with that you solve?",
"link": "https://www.onyx.app/9916329-2"
},
{
"text": "Mina Park: The most common pattern is: teams have lots of customer data, but it doesn\u2019t translate into a shared view of \u201cthese are our highest-fit segments\u201d and \u201cthis is the message that actually resonates with them.\u201d So Customer_Analyzer does two things: it helps you prioritize segments with a fit score, and then it generates positioning recommendations\u2014editable messaging blocks\u2014based on the fit drivers and the actual language customers use.",
"link": "https://www.onyx.app/9916329-3"
},
{
"text": "Natalie Collins: Positioning recommendations\u2014like copy suggestions? Or more like a framework?",
"link": "https://www.onyx.app/9916329-4"
},
{
"text": "Mina Park: Both. The feature I\u2019d highlight is our positioning_recommendation module. Once you\u2019ve identified a prioritized segment, it outputs a segment-specific value proposition, top use cases, differentiators, proof points you should collect, pricing/value framing notes, and an objection-handling playbook. And it\u2019s not just a generic doc\u2014it\u2019s packaged as usable blocks: website hero copy, sales deck bullets, email openers. Each block links back to the underlying drivers and evidence so product, marketing, and sales can all see \u201cwhy this claim is true.\u201d",
"link": "https://www.onyx.app/9916329-5"
},
{
"text": "Natalie Collins: Interesting. We\u2019ve been trying to tighten ICP and messaging, but it tends to be opinion-based. How does it decide what\u2019s \u201chigh fit\u201d?",
"link": "https://www.onyx.app/9916329-6"
},
{
"text": "Mina Park: Great question. We ingest your existing signals\u2014whatever you trust today: CRM attributes, onboarding responses, usage events, support patterns, and notes from calls. Then we identify the drivers that correlate with success outcomes you care about\u2014things like time-to-value, expansion, retention, or faster deal cycles. The fit score is essentially \u201chow closely does this segment match what your product is currently best at delivering,\u201d and it\u2019s transparent: you can see which drivers contributed and which data points supported it.",
"link": "https://www.onyx.app/9916329-7"
},
{
"text": "Natalie Collins: Okay. We\u2019re a smaller team\u2014Ultraviolet Summit Labs doesn\u2019t have a huge ops function. I\u2019m worried this becomes a big data project.",
"link": "https://www.onyx.app/9916329-8"
},
{
"text": "Mina Park: Totally fair. We\u2019re designed for \u201cstart small.\u201d Most teams begin with two or three sources\u2014often CRM + product usage + a lightweight survey\u2014or even CRM + interview notes. You can get value without perfect instrumentation. The point is to move from scattered inputs to a prioritized segment view and messaging you can immediately test in outbound, on the website, and in discovery calls.",
"link": "https://www.onyx.app/9916329-9"
},
{
"text": "Natalie Collins: You mentioned product teams\u2014my main focus is product planning. How does this help roadmap decisions?",
"link": "https://www.onyx.app/9916329-10"
},
{
"text": "Mina Park: That\u2019s one of the strongest use cases: product planning. Because once you see which segments are highest fit and what their unmet needs are\u2014based on patterns in tickets, usage drop-offs, interview language\u2014you can make cleaner roadmap bets. Instead of \u201cwe should build X because we heard it twice,\u201d you can say \u201cthis is a high-fit segment with consistent unmet need around Y; solving Y improves retention and expands the addressable footprint inside that segment.\u201d",
"link": "https://www.onyx.app/9916329-11"
},
{
"text": "Natalie Collins: That\u2019s the kind of evidence we\u2019ve been missing. But how do you prevent it from just reflecting current customers and missing where we want to go?",
"link": "https://www.onyx.app/9916329-12"
},
{
"text": "Mina Park: We handle that in two ways. First, you can define the outcomes you want to optimize for\u2014like expansion potential or enterprise readiness\u2014so the fit scoring doesn\u2019t over-index on \u201ceasy small customers\u201d if that\u2019s not strategic. Second, positioning recommendations explicitly include \u201cproof points to collect\u201d and \u201cobjections to address,\u201d which is helpful when you\u2019re moving upmarket or into a newer segment\u2014so you can run targeted experiments and build evidence rather than guessing.",
"link": "https://www.onyx.app/9916329-13"
},
{
"text": "Natalie Collins: Got it. What would an example output look like for a segment?",
"link": "https://www.onyx.app/9916329-14"
},
{
"text": "Mina Park: Say you identify a segment like \u201cmid-market teams with high onboarding completion but recurring support requests around integrations.\u201d The output might recommend a value proposition emphasizing faster time-to-value with fewer workflow breaks, differentiators around your integration approach, proof points like integration setup time or support deflection, plus an objection-handling playbook\u2014for example, \u201cwe already have tooling for this\u201d or \u201cyour platform seems complex.\u201d And then it gives you ready-to-edit blocks: a homepage hero line, 3 sales deck bullets, and an email opener tailored to that segment.",
"link": "https://www.onyx.app/9916329-15"
},
{
"text": "Natalie Collins: That\u2019s useful. We constantly rewrite messaging and still aren\u2019t sure it matches what customers actually care about.",
"link": "https://www.onyx.app/9916329-16"
},
{
"text": "Mina Park: Exactly. And because the recommendations are tied back to observed customer language\u2014pulled from tickets, surveys, and notes\u2014you get that \u201cthis is how customers describe the problem\u201d angle, not internal jargon. That tends to tighten marketing and sales alignment fast.",
"link": "https://www.onyx.app/9916329-17"
},
{
"text": "Natalie Collins: What does implementation look like? Like timeline and effort.",
"link": "https://www.onyx.app/9916329-18"
},
{
"text": "Mina Park: Typically, week one is connecting the first sources\u2014often CRM and one other system\u2014and agreeing on the success outcomes you want to model. Week two is producing the first cut of fit scoring and a couple prioritized segments. Then we generate positioning recommendations for those segments and run a working session to edit and turn them into assets you can deploy. Most teams see something actionable in the first two weeks, without a big IT lift.",
"link": "https://www.onyx.app/9916329-19"
},
{
"text": "Natalie Collins: And is this replacing what we do in research? Or augmenting it?",
"link": "https://www.onyx.app/9916329-20"
},
{
"text": "Mina Park: Augmenting. It\u2019s not trying to replace qualitative discovery; it helps you scale it. It turns scattered qualitative and quantitative signals into a coherent picture, then gives you a structured way to translate that into messaging and product bets. Many teams use it to decide where to do deeper interviews next\u2014because you can see which segments are high fit but poorly understood, or where unmet needs cluster.",
"link": "https://www.onyx.app/9916329-21"
},
{
"text": "Natalie Collins: This sounds like something our product marketing and sales leads would also want to see. I\u2019m not the only stakeholder.",
"link": "https://www.onyx.app/9916329-22"
},
{
"text": "Mina Park: That\u2019s perfect, and honestly that\u2019s when it works best\u2014when product, marketing, and sales can look at the same drivers and messaging. Would it be reasonable to set up a 45-minute session next week with you plus whoever owns positioning\u2014maybe product marketing\u2014and someone from sales? I can walk through a sample workflow, show the positioning blocks, and we can talk through your current segments and what data you already have.",
"link": "https://www.onyx.app/9916329-23"
},
{
"text": "Natalie Collins: Yes, I think that makes sense. If you can keep it practical\u2014less \u201ctheory,\u201d more \u201chere\u2019s what you\u2019d get.\u201d",
"link": "https://www.onyx.app/9916329-24"
},
{
"text": "Mina Park: Absolutely. I\u2019ll tailor it. Before we schedule, two quick questions so I bring the right examples: one, what\u2019s the biggest product planning decision you\u2019re trying to make this quarter\u2014new segment, expansion, retention? Two, what systems do you have today for customer signals\u2014CRM, analytics, support?",
"link": "https://www.onyx.app/9916329-25"
},
{
"text": "Natalie Collins: This quarter we\u2019re debating whether to double down on a segment we\u2019re seeing traction with, versus chasing a newer one. Systems-wise: we\u2019re on a standard CRM, we have product usage events in an analytics tool, and support tickets in a helpdesk platform. We also have a bunch of call notes in docs.",
"link": "https://www.onyx.app/9916329-26"
},
{
"text": "Mina Park: Perfect\u2014that\u2019s exactly the mix we work with. I\u2019ll set the agenda around segment prioritization for that decision, then generate sample positioning recommendations for the top two segments so you can compare. What does your calendar look like Tuesday or Wednesday afternoon?",
"link": "https://www.onyx.app/9916329-27"
},
{
"text": "Natalie Collins: Wednesday afternoon could work. Send me a couple options and I\u2019ll loop in our product marketing lead.",
"link": "https://www.onyx.app/9916329-28"
},
{
"text": "Mina Park: Great. I\u2019ll email two times for Wednesday afternoon and include a brief outline: inputs we\u2019d use, what the fit score shows, and example positioning blocks\u2014value prop, differentiators, and objection handling. Thanks Natalie\u2014looking forward to it.",
"link": "https://www.onyx.app/9916329-29"
},
{
"text": "Natalie Collins: Sounds good. Thanks, Mina.",
"link": "https://www.onyx.app/9916329-30"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "mina_park@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "natalie_collins@ultraviolet_summit_labs.onyx.app"
}
]
}

View File

@@ -0,0 +1,153 @@
{
"id": "FIREFLIES_7297025",
"semantic_identifier": "7297025 - Dunecrown Mining - sales_pitch - Discussion with Bianca Ramirez",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-04-06 00:00:00",
"metadata": {
"meeting_date": "2025-04-06 00:00:00",
"duration_min": "26"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-04"
],
"year_month": "2025-04",
"meeting_title": "7297025 - Dunecrown Mining - sales_pitch - Discussion with Bianca Ramirez",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hi Bianca, thanks for taking a few minutes\u2014James Choi here. I know you\u2019re busy, so I\u2019ll keep this tight. I\u2019m calling because a lot of support ops teams are drowning in duplicate tickets and \u201csame issue, different wording\u201d requests, and it makes it hard to tell Product what\u2019s actually trending. We built Issue_Tracker specifically to sit between Zendesk/Intercom and Jira/Linear and turn raw customer noise into clear, prioritized issue themes. Mind if I ask how you\u2019re handling that today at Dunecrown Mining?",
"link": "https://www.onyx.app/3685596-1"
},
{
"text": "Bianca Ramirez: Hi James\u2014sure. We\u2019re definitely seeing a lot of repeats, especially around a few product areas. We tag and categorize in Zendesk, and then we manually summarize for our weekly handoff to Product. It works, but it\u2019s time-consuming and we still miss patterns.",
"link": "https://www.onyx.app/3685596-2"
},
{
"text": "James Choi: That\u2019s exactly the pain we hear. The core thing Issue_Tracker does is automatic issue clustering\u2014so it ingests tickets, chats, emails, even call-note summaries, and deduplicates them into issue clusters using semantic similarity plus rules you can control, like product area, severity, keywords, or platform. Instead of 5,000 tickets feeling like 5,000 separate problems, you end up with, say, ~80 active themes you can actually manage. When you say you miss patterns, is it because the volume is too high, or because customers describe the same issue in different ways?",
"link": "https://www.onyx.app/3685596-3"
},
{
"text": "Bianca Ramirez: Both. Volume is a big factor, but also people describe things differently. Sometimes they file it as a bug, sometimes as a feature request, and it splinters across tags. Then engineering thinks it\u2019s small because each bucket looks small.",
"link": "https://www.onyx.app/3685596-4"
},
{
"text": "James Choi: Right\u2014Issue_Tracker is built to prevent that \u201cfalse smallness.\u201d The clustering creates a standardized \u201cissue object\u201d with the theme, suspected root cause category\u2014bug vs UX vs missing feature vs integration\u2014plus evidence and links back to the source conversations in Zendesk. So when you go to engineering, you\u2019re not saying \u201cwe think this matters,\u201d you\u2019re showing: here\u2019s the cluster, here are representative examples, here are the top phrases customers use, and here\u2019s the list of affected customers.",
"link": "https://www.onyx.app/3685596-5"
},
{
"text": "Bianca Ramirez: That sounds useful. How much manual work is involved? We don\u2019t want another tool that requires constant babysitting.",
"link": "https://www.onyx.app/3685596-6"
},
{
"text": "James Choi: Totally fair. Most teams start with the automatic clustering and then do light human refinement using a merge/split workflow with guardrails. So if two clusters are basically the same, you merge; if one cluster is too broad, you split. The system suggests cluster names, highlights representative tickets, and keeps the underlying links intact so nothing becomes a dead end. The goal is: your team spends minutes curating the top themes, not hours sorting every ticket.",
"link": "https://www.onyx.app/3685596-7"
},
{
"text": "Bianca Ramirez: Okay. And how does this fit with our existing process? We use Zendesk heavily, and then Jira for engineering work.",
"link": "https://www.onyx.app/3685596-8"
},
{
"text": "James Choi: Issue_Tracker is designed to sit between them rather than replace either. Zendesk stays your system of record for support interactions; Jira stays engineering\u2019s system of record for delivery. We pull in the customer-reported inputs, cluster them into themes, and then you can route the top clusters to your weekly Product/Engineering sync with one click\u2014each cluster carries the evidence bundle and the affected customer list. Then when engineering creates or links a Jira issue, Issue_Tracker keeps that status connected back to the cluster so Support can see what\u2019s in progress, what\u2019s shipped, and what\u2019s stalled\u2014without digging through Jira tickets.",
"link": "https://www.onyx.app/3685596-9"
},
{
"text": "Bianca Ramirez: That \u201cstatus connected back\u201d part would help. Our agents get asked for updates and it\u2019s hard to give a consistent answer.",
"link": "https://www.onyx.app/3685596-10"
},
{
"text": "James Choi: Exactly\u2014because the issue object stays linked to both the original conversations and the downstream engineering work. So you can search, \u201cWhat\u2019s the status of the theme that\u2019s driving all these tickets?\u201d and you get a defensible history: when it was first reported, how it evolved, what decisions were made, and what\u2019s currently happening. That usually reduces internal back-and-forth and also helps you set clearer customer expectations.",
"link": "https://www.onyx.app/3685596-11"
},
{
"text": "Bianca Ramirez: Do you have an example of how support ops teams actually use it week to week?",
"link": "https://www.onyx.app/3685596-12"
},
{
"text": "James Choi: Yes\u2014one very common workflow is: support ops clusters, say, 5,000 monthly tickets into around 80 active themes. During the week, the clustering updates continuously as new tickets come in. Then before the Product/Engineering weekly meeting, you take the top 10 clusters\u2014based on volume, severity, or customer impact\u2014send those over with representative examples and affected customer lists, and keep the rest as monitored themes. The team stops arguing about anecdotes and starts prioritizing based on a single shared view of \u201cwhat\u2019s happening to customers.\u201d",
"link": "https://www.onyx.app/3685596-13"
},
{
"text": "Bianca Ramirez: That sounds like what we want. Our biggest challenge is making the case that something is widespread, not a one-off.",
"link": "https://www.onyx.app/3685596-14"
},
{
"text": "James Choi: That\u2019s the sweet spot. And because the clusters show volume and trends over time, you can say, \u201cThis issue is up 40% week-over-week,\u201d or \u201cThis theme affects these customers,\u201d with direct links back to the source tickets. It\u2019s hard to dismiss because the evidence is right there, and it\u2019s not dependent on perfect tagging discipline.",
"link": "https://www.onyx.app/3685596-15"
},
{
"text": "Bianca Ramirez: How long does it take to get value? We\u2019re mid-quarter and I can\u2019t take on a long implementation.",
"link": "https://www.onyx.app/3685596-16"
},
{
"text": "James Choi: Understood. The typical path is: connect Zendesk, let it ingest historical and new tickets, and within the first week you\u2019ll see clusters forming and can start using them in your handoffs. Then you tune a few rules\u2014like product areas or severity\u2014and that\u2019s usually incremental. If it\u2019s helpful, on a full call we can map your current weekly triage workflow and show what it would look like with Issue_Tracker layered in, without disrupting Zendesk or Jira.",
"link": "https://www.onyx.app/3685596-17"
},
{
"text": "Bianca Ramirez: I\u2019d be open to that. I\u2019d want to see how well it clusters our real tickets, because our terminology is a bit unique.",
"link": "https://www.onyx.app/3685596-18"
},
{
"text": "James Choi: Completely reasonable\u2014seeing your data is the only way to judge clustering quality. On the next call, we can do a quick discovery on your current categories and then walk through a sample output: how it names clusters, what it picks as representative examples, and how the merge/split workflow works when terminology is unique. If it doesn\u2019t group things the way you\u2019d expect, we\u2019ll be transparent about that.",
"link": "https://www.onyx.app/3685596-19"
},
{
"text": "Bianca Ramirez: Okay. Who else should be on that call from your side?",
"link": "https://www.onyx.app/3685596-20"
},
{
"text": "James Choi: It would just be me, and if you want a deeper technical walkthrough we can add someone later\u2014but for the first full sales call, I\u2019d keep it focused: your support ops perspective, how you currently hand off to Product/Engineering, and what \u201csuccess\u201d would look like at Dunecrown. Would it make sense to include whoever runs the weekly triage meeting, or a PM who consumes the insights?",
"link": "https://www.onyx.app/3685596-21"
},
{
"text": "Bianca Ramirez: Yes, I can bring our support ops lead and maybe one PM. Can you send times?",
"link": "https://www.onyx.app/3685596-22"
},
{
"text": "James Choi: Absolutely. I\u2019ll email a couple options\u2014does next Tuesday or Wednesday afternoon work better for a 30-minute session? We\u2019ll aim to confirm: ticket volume, current tagging, how you route themes to engineering, and then we\u2019ll show what Issue_Tracker would produce\u2014top clusters, evidence bundles, and affected customer lists.",
"link": "https://www.onyx.app/3685596-23"
},
{
"text": "Bianca Ramirez: Wednesday afternoon is usually better. Send a couple slots and I\u2019ll coordinate.",
"link": "https://www.onyx.app/3685596-24"
},
{
"text": "James Choi: Perfect. I\u2019ll send two Wednesday options and a short agenda. Last question so I tailor it\u2014roughly how many tickets a month are you seeing in Zendesk right now?",
"link": "https://www.onyx.app/3685596-25"
},
{
"text": "Bianca Ramirez: Around 4,000 to 6,000 depending on the month.",
"link": "https://www.onyx.app/3685596-26"
},
{
"text": "James Choi: Great\u2014that\u2019s right in the range where clustering pays off quickly. I\u2019ll follow up with the invite options, and if you can reply with any current tag taxonomy or a sample of your weekly summary format, I\u2019ll align the demo to that. Thanks, Bianca.",
"link": "https://www.onyx.app/3685596-27"
},
{
"text": "Bianca Ramirez: Sounds good. Thanks, James\u2014talk soon.",
"link": "https://www.onyx.app/3685596-28"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "bianca_ramirez@dunecrown_mining.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_9140779",
"semantic_identifier": "9140779 - Valeforge Automotive - sales_pitch - Discussion with Parker Chapman",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-04-03 00:00:00",
"metadata": {
"meeting_date": "2025-04-03 00:00:00",
"duration_min": "30"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-04"
],
"year_month": "2025-04",
"meeting_title": "9140779 - Valeforge Automotive - sales_pitch - Discussion with Parker Chapman",
"organizer_email": "irene_shen@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Irene Shen: Hi Parker\u2014this is Irene Shen. Thanks for taking a few minutes. I work with teams like yours at Valeforge Automotive on tightening up who to target and how to message to them. Is now still good for a quick intro?",
"link": "https://www.onyx.app/8805486-1"
},
{
"text": "Parker Chapman: Yep, I\u2019ve got a few minutes. What\u2019s this about?",
"link": "https://www.onyx.app/8805486-2"
},
{
"text": "Irene Shen: Perfect. I\u2019ll keep it tight. The product is Customer_Analyzer. It pulls together customer signals you already have\u2014CRM fields, onboarding notes, product usage, support tickets\u2014and turns that into two things: a clear fit score by segment, and practical positioning recommendations so sales and marketing aren\u2019t guessing. When I looked at your space, one use case that tends to hit is deciding which industries to target next quarter using retention and support burden signals. Is that a conversation happening internally right now?",
"link": "https://www.onyx.app/8805486-3"
},
{
"text": "Parker Chapman: Yeah, we\u2019re debating a couple industries. Retention\u2019s decent overall, but support load is all over the place depending on customer type.",
"link": "https://www.onyx.app/8805486-4"
},
{
"text": "Irene Shen: That\u2019s exactly the pattern we see. What usually happens is teams pick the \u201cbiggest\u201d segment or the loudest feedback, and then six months later you realize churn or support costs wipe out the revenue. Customer_Analyzer scores segments not just on revenue potential, but on \u201cfit drivers\u201d you define\u2014things like retention, expansion, time-to-value, and support burden\u2014using the data you already have.",
"link": "https://www.onyx.app/8805486-5"
},
{
"text": "Parker Chapman: We have most of that in Salesforce and Zendesk, but it\u2019s messy. How much work is it to set up?",
"link": "https://www.onyx.app/8805486-6"
},
{
"text": "Irene Shen: Low lift to start. We typically connect your CRM and support system, then you choose the signals that matter for the decision\u2014like retention by industry, ticket volume per account, and maybe onboarding friction. The system normalizes it into a segment view. You don\u2019t need a perfect data warehouse on day one; it\u2019s meant to work with \u201creal\u201d data and still give you directional clarity.",
"link": "https://www.onyx.app/8805486-7"
},
{
"text": "Parker Chapman: Okay. We can build dashboards for some of that, though. What\u2019s different here?",
"link": "https://www.onyx.app/8805486-8"
},
{
"text": "Irene Shen: Great question. Dashboards tell you what happened; they don\u2019t tell you what to do with it\u2014especially when marketing and sales need a narrative. The differentiator is the positioning_recommendation feature. Once you\u2019ve identified, say, \u201cSegment A\u201d is high retention but high support, and \u201cSegment B\u201d is slightly lower retention but low support and faster time-to-value, Customer_Analyzer generates segment-specific messaging blocks: value proposition, top use cases, differentiators, proof points to collect, even objection handling and pricing/value framing notes.",
"link": "https://www.onyx.app/8805486-9"
},
{
"text": "Parker Chapman: Generated messaging? Like AI copy?",
"link": "https://www.onyx.app/8805486-10"
},
{
"text": "Irene Shen: It\u2019s more structured than \u201cAI writes you a homepage.\u201d It\u2019s grounded in the drivers and evidence from your own signals and language\u2014like phrases that show up in support tickets, interview notes, and onboarding responses. And it\u2019s editable. The output is packaged as blocks you can drop into a sales deck, website hero, or outbound email opener\u2014plus it links back to the \u201cwhy,\u201d so your team can trust it and refine it.",
"link": "https://www.onyx.app/8805486-11"
},
{
"text": "Parker Chapman: Interesting. Our pain is that sales says one thing, marketing says another, and product is focused on a totally different customer type.",
"link": "https://www.onyx.app/8805486-12"
},
{
"text": "Irene Shen: Exactly. This is built to get alignment. Instead of debating opinions, you\u2019re debating evidence: \u201cWhich segments actually retain?\u201d \u201cWhich segments cost us the least to support?\u201d And then, \u201cWhat story should we tell to those segments to win more deals?\u201d It reduces the back-and-forth and speeds up campaigns and enablement because everyone\u2019s working from the same segment definition and the same positioning kit.",
"link": "https://www.onyx.app/8805486-13"
},
{
"text": "Parker Chapman: How do you handle industries specifically? We slice by NAICS but it\u2019s inconsistent.",
"link": "https://www.onyx.app/8805486-14"
},
{
"text": "Irene Shen: Totally common. We can start with whatever you have\u2014NAICS, self-reported industry, account tags\u2014then clean it progressively. But even before it\u2019s perfect, you can get strong signal by grouping into a handful of buckets you\u2019re already considering for next quarter. The goal isn\u2019t academic taxonomy; it\u2019s making a decision like, \u201cDo we lean into fleet maintenance providers or specialty parts distributors?\u201d based on retention plus support burden and time-to-value.",
"link": "https://www.onyx.app/8805486-15"
},
{
"text": "Parker Chapman: If we did this, what would we see in week one or two?",
"link": "https://www.onyx.app/8805486-16"
},
{
"text": "Irene Shen: In the first couple weeks, you\u2019d typically see: a ranked list of candidate segments with a fit score and the underlying drivers, plus a first draft of positioning recommendations for the top one or two segments. For example, if one industry is retaining well but generating lots of tickets around configuration, the messaging might emphasize guided onboarding and reliability, and the objection playbook might address \u201cimplementation complexity\u201d head-on with proof points you should collect.",
"link": "https://www.onyx.app/8805486-17"
},
{
"text": "Parker Chapman: That objection handling piece is appealing. We lose deals when prospects worry about rollout effort.",
"link": "https://www.onyx.app/8805486-18"
},
{
"text": "Irene Shen: Exactly\u2014and what\u2019s nice is it\u2019s not generic. It\u2019s tied to what your current customers are actually saying and what correlates with success. The playbook can include suggested language for reframing, and a shortlist of proof points\u2014like \u201caverage time-to-first-value by segment\u201d or \u201csupport tickets per month after month two\u201d\u2014so sales isn\u2019t scrambling for anecdotes.",
"link": "https://www.onyx.app/8805486-19"
},
{
"text": "Parker Chapman: Who on our side would need to be involved? I can\u2019t pull in ten people for a new tool right now.",
"link": "https://www.onyx.app/8805486-20"
},
{
"text": "Irene Shen: You can keep it tight. Usually it\u2019s one person who owns go-to-market ops or rev ops for the data connections, and one stakeholder each from marketing and sales enablement to validate the messaging blocks. If product wants to join later, great, but not required to start.",
"link": "https://www.onyx.app/8805486-21"
},
{
"text": "Parker Chapman: What does a \u201cfull\u201d evaluation look like? I\u2019m assuming that\u2019s what you\u2019re aiming for.",
"link": "https://www.onyx.app/8805486-22"
},
{
"text": "Irene Shen: Yes\u2014my goal today is just to see if it\u2019s worth a deeper session. In the full call, we\u2019d do two things: (1) map the segments you\u2019re debating for next quarter and the signals you trust for retention and support burden, and (2) walk through a sample output of positioning_recommendation\u2014value prop, differentiators, proof points, objection handling\u2014so you can see how it would plug into your website, deck, and outbound.",
"link": "https://www.onyx.app/8805486-23"
},
{
"text": "Parker Chapman: That sounds reasonable. Can you tailor it to automotive-related segments? Our buyers vary a lot.",
"link": "https://www.onyx.app/8805486-24"
},
{
"text": "Irene Shen: Absolutely. We\u2019ll use your actual segment candidates\u2014whatever you\u2019re considering\u2014and show how the recommendations differ. Often the \u201csame\u201d product needs totally different framing depending on whether the buyer cares about uptime, compliance, or operational efficiency. The tool makes those differences explicit and consistent.",
"link": "https://www.onyx.app/8805486-25"
},
{
"text": "Parker Chapman: Okay. If we did a 45-minute call, who should I bring?",
"link": "https://www.onyx.app/8805486-26"
},
{
"text": "Irene Shen: If you can bring whoever owns your segment targeting decision\u2014maybe you or your marketing lead\u2014and someone who can speak to support metrics, that\u2019s perfect. Even 2\u20133 people total is fine.",
"link": "https://www.onyx.app/8805486-27"
},
{
"text": "Parker Chapman: I can bring our demand gen manager and someone from support ops. When are you available?",
"link": "https://www.onyx.app/8805486-28"
},
{
"text": "Irene Shen: Great. I have Thursday at 11:00am or Friday at 2:30pm your time. On that call, I\u2019ll also share a quick checklist in advance\u2014just what systems you use and the top industries you\u2019re weighing\u2014so we can make it concrete.",
"link": "https://www.onyx.app/8805486-29"
},
{
"text": "Parker Chapman: Friday at 2:30 works. Send the invite and the checklist.",
"link": "https://www.onyx.app/8805486-30"
},
{
"text": "Irene Shen: Will do. Thanks, Parker\u2014looking forward to it. If you can also include one example of a segment that \u201clooks good on revenue\u201d but is painful on support, we\u2019ll use that as a real scenario in the walk-through.",
"link": "https://www.onyx.app/8805486-31"
},
{
"text": "Parker Chapman: Yep, I\u2019ve got a couple in mind. Send it over and we\u2019ll be ready.",
"link": "https://www.onyx.app/8805486-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "irene_shen@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "parker_chapman@valeforge_automotive.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_9498242",
"semantic_identifier": "9498242 - Umbraleaf Biotech - sales_pitch - Discussion with Larry Delgado",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-04-20 00:00:00",
"metadata": {
"meeting_date": "2025-04-20 00:00:00",
"duration_min": "30"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-04"
],
"year_month": "2025-04",
"meeting_title": "9498242 - Umbraleaf Biotech - sales_pitch - Discussion with Larry Delgado",
"organizer_email": "sneha_reddy@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Sneha Reddy: Hi Larry\u2014Sneha Reddy here. Thanks for taking a few minutes. I\u2019ll keep this short: I wanted to share how Umbraleaf Biotech could cut support noise and get a clearer, revenue-grounded view of what issues to fix next using our Issue_Tracker.",
"link": "https://www.onyx.app/5929235-1"
},
{
"text": "Larry Delgado: Hi Sneha. Sure\u2014what exactly is it, and how is it different from what we already do in Zendesk and Jira?",
"link": "https://www.onyx.app/5929235-2"
},
{
"text": "Sneha Reddy: Great question. Issue_Tracker isn\u2019t trying to replace Zendesk or Jira. It sits between them and standardizes what we call an \u201cissue object\u201d\u2014a theme or root cause with evidence, impacted customers, and links back to the original conversations and forward to the engineering work. So instead of hundreds of tickets and scattered Jira items, you get a deduped set of themes that stays connected to both sides.",
"link": "https://www.onyx.app/5929235-3"
},
{
"text": "Larry Delgado: Okay. We definitely have duplicate tickets. But we\u2019ve tried tagging and macros\u2014still messy. How does this actually help day to day?",
"link": "https://www.onyx.app/5929235-4"
},
{
"text": "Sneha Reddy: The most immediate win is clustering and deduplication. If you\u2019re getting, say, thousands of tickets a month, Issue_Tracker groups them into a manageable set of active themes\u2014think dozens, not thousands\u2014then merges duplicates and keeps representative examples. Support ops can route the top themes to product/engineering weekly with a clean packet: what\u2019s happening, how often, which customers are impacted, and the source links.",
"link": "https://www.onyx.app/5929235-5"
},
{
"text": "Larry Delgado: That sounds useful. But prioritization is the hard part for us. We argue about what\u2019s loud versus what matters.",
"link": "https://www.onyx.app/5929235-6"
},
{
"text": "Sneha Reddy: Exactly\u2014and that\u2019s where our explainable issue prioritization comes in. Issue_Tracker assigns each cluster a priority score based on inputs you control: frequency and velocity, severity tags, customer tier or ARR, churn risk flags, and segment importance. It\u2019s transparent\u2014so it\u2019s not a black box \u201cAI says fix this,\u201d it\u2019s \u201chere\u2019s why this cluster is ranked #3.\u201d",
"link": "https://www.onyx.app/5929235-7"
},
{
"text": "Larry Delgado: When you say \u201csegment importance,\u201d what does that mean for a biotech company like ours?",
"link": "https://www.onyx.app/5929235-8"
},
{
"text": "Sneha Reddy: You can weight issues based on your ideal customer profile and strategic segments\u2014so if a theme hits high-fit, high-value customers, it rises. And if you want, it can integrate with your CRM context so issues tied to revenue exposure or key accounts are naturally elevated. The goal is to align Support, Product, and Sales around the same logic instead of each team having their own list.",
"link": "https://www.onyx.app/5929235-9"
},
{
"text": "Larry Delgado: We do have different perspectives. Support is focused on SLA and ticket volume, product wants impact versus effort, and sales only escalates when a deal is at risk.",
"link": "https://www.onyx.app/5929235-10"
},
{
"text": "Sneha Reddy: We built for that reality. Issue_Tracker supports different views off the same underlying issue set: Support can see SLA risk and volume; Product can see impact signals; Sales can see deal blockers; Execs can see revenue exposure. Everyone\u2019s looking at the same clusters and evidence\u2014just filtered and scored for their workflow.",
"link": "https://www.onyx.app/5929235-11"
},
{
"text": "Larry Delgado: How painful is implementation? We don\u2019t have appetite for a big migration.",
"link": "https://www.onyx.app/5929235-12"
},
{
"text": "Sneha Reddy: It\u2019s light. Since we\u2019re not replacing Zendesk or Jira, there\u2019s no rip-and-replace. We connect to your support channel, ingest ticket and conversation data, then connect downstream to Jira so issues can link to epics or tickets you already use. You keep working where you work; Issue_Tracker becomes the triage and intelligence layer that keeps everything coherent.",
"link": "https://www.onyx.app/5929235-13"
},
{
"text": "Larry Delgado: What about false clustering? Sometimes two issues look similar but have different root causes.",
"link": "https://www.onyx.app/5929235-14"
},
{
"text": "Sneha Reddy: Totally fair. We handle that in two ways. First, clusters maintain traceability\u2014so you can inspect the underlying tickets and evidence quickly. Second, your team can split or merge clusters, and those decisions are remembered. Over time, you build a defensible history of what you considered \u201cthe same issue\u201d and why, which helps when teams revisit old debates six months later.",
"link": "https://www.onyx.app/5929235-15"
},
{
"text": "Larry Delgado: That \u201chistory of decisions\u201d piece is interesting. We lose context constantly when people change roles.",
"link": "https://www.onyx.app/5929235-16"
},
{
"text": "Sneha Reddy: Exactly. Issue_Tracker keeps a searchable record of the theme, impacted customers, status, linked Jira work, and the reasoning behind prioritization changes. So when an exec asks, \u201cWhy didn\u2019t we fix this last quarter?\u201d you can answer with evidence instead of tribal memory.",
"link": "https://www.onyx.app/5929235-17"
},
{
"text": "Larry Delgado: You mentioned a weekly packet to engineering. What does that look like?",
"link": "https://www.onyx.app/5929235-18"
},
{
"text": "Sneha Reddy: A typical support ops workflow is: the system clusters, then your ops lead reviews the top themes and pushes the top 10 to product/engineering each week. Each theme includes representative ticket examples, volume trends, severity distribution, and a list of impacted customers. Engineering gets fewer, higher-quality inputs; support stops forwarding noisy threads; product gets a prioritized queue grounded in customer and business impact.",
"link": "https://www.onyx.app/5929235-19"
},
{
"text": "Larry Delgado: That would help. Right now, we\u2019re sending long Slack threads and spreadsheets.",
"link": "https://www.onyx.app/5929235-20"
},
{
"text": "Sneha Reddy: That\u2019s exactly what we see most teams doing\u2014manual triage and re-triage. Our goal is to compress that into something consistent and easy to act on, while making sure the \u201cwhat matters\u201d conversation is anchored in revenue exposure and customer importance, not whoever shouts loudest.",
"link": "https://www.onyx.app/5929235-21"
},
{
"text": "Larry Delgado: What kind of results do you typically see?",
"link": "https://www.onyx.app/5929235-22"
},
{
"text": "Sneha Reddy: The common pattern is a big reduction in duplicate noise\u2014teams taking thousands of monthly tickets and managing them as a small set of active themes\u2014and faster alignment on priorities because the scoring is shared and explainable. It tends to reduce escalations and rework because product and engineering get clearer signals earlier.",
"link": "https://www.onyx.app/5929235-23"
},
{
"text": "Larry Delgado: This is sounding relevant. For us, the biggest pain is that critical customer issues don\u2019t always get surfaced until late, and then everyone scrambles.",
"link": "https://www.onyx.app/5929235-24"
},
{
"text": "Sneha Reddy: That\u2019s a perfect fit for the prioritization model\u2014especially if we incorporate customer tier, churn risk flags, and segment weighting. It makes \u201cquiet but high-risk\u201d issues rise. And because the themes stay linked back to the original conversations, your team can quickly show what customers are experiencing without hunting through systems.",
"link": "https://www.onyx.app/5929235-25"
},
{
"text": "Larry Delgado: Okay. What would you need from us to see if this would work?",
"link": "https://www.onyx.app/5929235-26"
},
{
"text": "Sneha Reddy: For a first step, I\u2019d suggest a 30-minute working session. We\u2019d map your current flow\u2014Zendesk to Jira, how you tag severity, how you define customer tiers\u2014and identify the inputs that should drive your priority score. If you\u2019re open to it, we can also look at a small sample of recent tickets to show how clustering would reduce noise and what the top themes would look like.",
"link": "https://www.onyx.app/5929235-27"
},
{
"text": "Larry Delgado: That seems reasonable. I\u2019d want someone from support ops and maybe a product ops person on that call.",
"link": "https://www.onyx.app/5929235-28"
},
{
"text": "Sneha Reddy: Perfect. If you bring support ops and product ops, I\u2019ll tailor the demo to your workflows: a Support view for SLA risk and volume, and a Product view focused on impact signals. We\u2019ll keep it practical\u2014how you\u2019d run your weekly routing, and how engineering would consume it.",
"link": "https://www.onyx.app/5929235-29"
},
{
"text": "Larry Delgado: All right\u2014let\u2019s do it. When can you meet?",
"link": "https://www.onyx.app/5929235-30"
},
{
"text": "Sneha Reddy: I have Thursday at 11am or Friday at 2pm. Which works better for you?",
"link": "https://www.onyx.app/5929235-31"
},
{
"text": "Larry Delgado: Friday at 2pm works. Send an invite and I\u2019ll loop in the right folks.",
"link": "https://www.onyx.app/5929235-32"
},
{
"text": "Sneha Reddy: Great\u2014Friday at 2pm it is. I\u2019ll send a calendar invite with a couple of quick questions in advance\u2014mainly what support tool you\u2019re on, what Jira project structure looks like, and how you segment customers\u2014so we can make the session productive.",
"link": "https://www.onyx.app/5929235-33"
},
{
"text": "Larry Delgado: Sounds good. Looking forward to it.",
"link": "https://www.onyx.app/5929235-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "sneha_reddy@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "larry_delgado@umbraleaf_biotech.onyx.app"
}
]
}

View File

@@ -0,0 +1,145 @@
{
"id": "FIREFLIES_1356215",
"semantic_identifier": "1356215 - Birch & Beam Architects - sales_pitch - Discussion with Diego Arnold",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-20 00:00:00",
"metadata": {
"meeting_date": "2025-05-20 00:00:00",
"duration_min": "25"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "1356215 - Birch & Beam Architects - sales_pitch - Discussion with Diego Arnold",
"organizer_email": "camila_vega@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Camila Vega: Hi Diego, Camila Vega here\u2014thanks for taking a few minutes. I\u2019ll keep this brief. I\u2019m calling because we work with teams that are trying to get really clear on who their best customers are and stop wasting time on \u201cmaybe\u201d leads. Does that hit anything you\u2019re dealing with at Birch & Beam Architects?",
"link": "https://www.onyx.app/4844763-1"
},
{
"text": "Diego Arnold: Hey Camila. We\u2019re in the middle of a few initiatives, but sure\u2014what are you talking about specifically?",
"link": "https://www.onyx.app/4844763-2"
},
{
"text": "Camila Vega: Totally. The product is called Customer_Analyzer. It pulls together the signals you already have\u2014CRM fields, onboarding notes, project outcomes, support or change-order requests, even interview notes\u2014and turns them into a simple fit score by account and by segment. The goal is to help you define an ICP you can actually trust, so marketing and sales aren\u2019t guessing.",
"link": "https://www.onyx.app/4844763-3"
},
{
"text": "Diego Arnold: We\u2019re not a SaaS company. We\u2019re an architecture firm. Not sure how relevant an \u201cICP\u201d tool is for us.",
"link": "https://www.onyx.app/4844763-4"
},
{
"text": "Camila Vega: Fair question. When we say ICP, we mean \u201cbest-fit client profile.\u201d In your case it could be things like project type, client size, timeline realism, decision process, budget reliability, revision load\u2014whatever predicts profitable projects and smoother delivery. Customer_Analyzer\u2019s fit estimation feature gives you a 0\u2013100 fit score and a driver breakdown\u2014so you can see why a client is high fit or low fit and compare segments like commercial developers vs. institutional clients.",
"link": "https://www.onyx.app/4844763-5"
},
{
"text": "Diego Arnold: We already have a general sense of what good clients look like. It\u2019s not super scientific, but we know.",
"link": "https://www.onyx.app/4844763-6"
},
{
"text": "Camila Vega: That makes sense\u2014and honestly most teams do. Where we see Customer_Analyzer help is when the \u201cgeneral sense\u201d breaks down as you grow, add new services, or pursue new verticals. For example, if you\u2019re considering scaling outbound or paid acquisition, it validates which segment actually retains well and expands\u2014rather than just \u201ccloses.\u201d Then the fit estimation highlights the drivers: time-to-value, support burden, revision cycles, payment issues\u2014so you can operationalize it.",
"link": "https://www.onyx.app/4844763-7"
},
{
"text": "Diego Arnold: We don\u2019t do paid acquisition, and we\u2019re not really scaling the way a startup is. We\u2019re mostly referral-driven.",
"link": "https://www.onyx.app/4844763-8"
},
{
"text": "Camila Vega: Got it\u2014so referrals are primary. Even then, the same problem can show up as inconsistent project profitability or teams getting stretched thin because the wrong work sneaks in. The other output is positioning recommendations\u2014basically messaging guidance based on what high-fit clients value and what objections come up. That can show up on your website, proposals, or qualification scripts.",
"link": "https://www.onyx.app/4844763-9"
},
{
"text": "Diego Arnold: We just refreshed our website and proposal templates. And messaging isn\u2019t our main pain right now.",
"link": "https://www.onyx.app/4844763-10"
},
{
"text": "Camila Vega: Understood. Let me ask one quick diagnostic\u2014are you seeing any patterns in projects that become time sinks? Like certain client types that create lots of scope churn, change orders, or delays?",
"link": "https://www.onyx.app/4844763-11"
},
{
"text": "Diego Arnold: Sure, some clients are harder than others. But it\u2019s not something we\u2019re going to buy software for. We handle it in process.",
"link": "https://www.onyx.app/4844763-12"
},
{
"text": "Camila Vega: That\u2019s fair. The reason I ask is the fit estimation can quantify those patterns from your existing data\u2014say, which attributes correlate with smoother delivery and repeat work. Even a lightweight setup can show, \u201cThese 3 drivers predict the projects that renew or expand,\u201d versus the ones that churn you out with headaches.",
"link": "https://www.onyx.app/4844763-13"
},
{
"text": "Diego Arnold: I\u2019m also thinking about the effort. We don\u2019t have a bunch of clean data lying around, and I don\u2019t have time to instrument everything.",
"link": "https://www.onyx.app/4844763-14"
},
{
"text": "Camila Vega: Totally hear you. We don\u2019t require heavy instrumentation to start. Many teams begin with what they already have: CRM fields, a few tags on project type, basic timeline/budget outcomes, maybe a short survey. Then Customer_Analyzer produces segment comparisons and a recommended best-fit profile based on observed outcomes. But if data isn\u2019t centralized, it can feel like work up front.",
"link": "https://www.onyx.app/4844763-15"
},
{
"text": "Diego Arnold: Yeah. And honestly, we\u2019re trying to reduce tools, not add another dashboard.",
"link": "https://www.onyx.app/4844763-16"
},
{
"text": "Camila Vega: Makes sense. If we kept this to a single outcome\u2014like \u201cidentify the top 2 client segments to prioritize and a short qualification checklist\u201d\u2014would that be worth a 30-minute deeper look? I can show exactly how the fit score and driver breakdown works with a sample dataset.",
"link": "https://www.onyx.app/4844763-17"
},
{
"text": "Diego Arnold: I don\u2019t think so. We\u2019ve got internal planning for the next couple weeks, and this doesn\u2019t feel like a priority.",
"link": "https://www.onyx.app/4844763-18"
},
{
"text": "Camila Vega: No problem. Before I let you go, is there someone else on your side who owns pipeline quality or client selection\u2014maybe whoever runs business development or operations\u2014who would be a better fit for this conversation?",
"link": "https://www.onyx.app/4844763-19"
},
{
"text": "Diego Arnold: Not really. We\u2019re pretty small, and those decisions are shared. If it becomes a pain later, we\u2019ll revisit, but right now we\u2019re set.",
"link": "https://www.onyx.app/4844763-20"
},
{
"text": "Camila Vega: Understood. Last thing\u2014would it be okay if I sent a one-pager that summarizes the fit estimation output and how it\u2019s used to validate an ideal client profile? If it resonates later, you can forward it internally.",
"link": "https://www.onyx.app/4844763-21"
},
{
"text": "Diego Arnold: Sure, you can send something. But I can\u2019t promise I\u2019ll look at it soon.",
"link": "https://www.onyx.app/4844763-22"
},
{
"text": "Camila Vega: That\u2019s completely fine. What\u2019s the best email for you?",
"link": "https://www.onyx.app/4844763-23"
},
{
"text": "Diego Arnold: Use diego@birchbeam.com.",
"link": "https://www.onyx.app/4844763-24"
},
{
"text": "Camila Vega: Perfect. I\u2019ll send a short overview and a couple example screenshots of the fit score and segment comparison. If in a quarter you\u2019re revisiting what work to pursue\u2014or noticing certain client types dragging margins\u2014we can pick it back up. Thanks again for your time, Diego.",
"link": "https://www.onyx.app/4844763-25"
},
{
"text": "Diego Arnold: Thanks, Camila. Take care.",
"link": "https://www.onyx.app/4844763-26"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "camila_vega@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "diego_arnold@birch_&_beam_architects.onyx.app"
}
]
}

View File

@@ -0,0 +1,153 @@
{
"id": "FIREFLIES_2113305",
"semantic_identifier": "2113305 - Horizoncrest Systems - sales_pitch - Discussion with Julian Morgan",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-12 00:00:00",
"metadata": {
"meeting_date": "2025-05-12 00:00:00",
"duration_min": "37"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "2113305 - Horizoncrest Systems - sales_pitch - Discussion with Julian Morgan",
"organizer_email": "camila_vega@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Camila Vega: Hi Julian\u2014Camila Vega here. Thanks for taking a few minutes. I work with teams that are getting buried in customer issues across Zendesk, Intercom, call notes, and then trying to translate that into Jira without losing context. I wanted to quickly share what Issue_Tracker does and see if it\u2019s worth a longer working session.",
"link": "https://www.onyx.app/8135367-1"
},
{
"text": "Julian Morgan: Hey Camila, sure. We definitely feel that pain\u2014things get scattered fast. What are you seeing with companies like us?",
"link": "https://www.onyx.app/8135367-2"
},
{
"text": "Camila Vega: Most teams have two problems at once: first, the same issue shows up in dozens of slightly different tickets and chats, so triage is repetitive and the true volume is hidden. Second, Sales and Support don\u2019t have a reliable \u201csource of truth\u201d for what\u2019s actually happening with an issue\u2014status, who it impacts, what we can safely tell customers\u2014so deals and renewals get risky at the worst time.",
"link": "https://www.onyx.app/8135367-3"
},
{
"text": "Julian Morgan: That sounds familiar. We\u2019re on Zendesk and Jira, and it\u2019s a lot of manual tagging and Slack threads. How would Issue_Tracker fit in without replacing those?",
"link": "https://www.onyx.app/8135367-4"
},
{
"text": "Camila Vega: Great question. Issue_Tracker sits between your support channels and Jira. It doesn\u2019t replace Zendesk or Jira\u2014it standardizes an \u201cissue object\u201d that links back to the original conversations and forward to the engineering work. The core capability is issue clustering: it ingests raw customer inputs\u2014tickets, chats, emails, even call-note summaries\u2014then automatically deduplicates and clusters them into themes using semantic similarity plus configurable rules like product area, platform, severity, or keywords.",
"link": "https://www.onyx.app/8135367-5"
},
{
"text": "Julian Morgan: Clustering sounds nice in theory, but we\u2019ve tried auto-tagging before and it got noisy. How do you keep it from being a mess?",
"link": "https://www.onyx.app/8135367-6"
},
{
"text": "Camila Vega: Totally\u2014if it\u2019s a black box, it fails. Two things make it workable. First, the clusters come with transparent evidence: representative examples, top phrases, and links back to the source Zendesk tickets or Intercom threads, so your team can validate quickly. Second, there\u2019s a merge/split workflow with guardrails\u2014so you can correct clusters without starting from scratch, and the system learns the boundaries you care about, like separating \u201cmobile login\u201d from \u201cSSO login\u201d even if users describe them similarly.",
"link": "https://www.onyx.app/8135367-7"
},
{
"text": "Julian Morgan: Okay, that would help. But our bigger issue lately is prioritization\u2014everything feels urgent, and we\u2019re not always sure what\u2019s actually impacting revenue.",
"link": "https://www.onyx.app/8135367-8"
},
{
"text": "Camila Vega: That\u2019s exactly where Issue_Tracker becomes a revenue-protection layer, not just a support analytics tool. Each cluster maintains impacted customers and context\u2014suspected root cause category like bug vs UX vs missing feature\u2014and then you can prioritize based on business impact: revenue exposure, segment or ICP importance, and deal risk. The use case we see a lot is Sales preventing deal slippage: a late-stage opportunity gets tied to a known issue cluster, and you have current status, ETA notes, and approved language on what\u2019s safe to communicate.",
"link": "https://www.onyx.app/8135367-9"
},
{
"text": "Julian Morgan: That would be useful. Right now Sales pings Support, Support pings Product, Product pings Engineering, and nobody wants to put anything in writing.",
"link": "https://www.onyx.app/8135367-10"
},
{
"text": "Camila Vega: Exactly. Instead of a dozen one-off escalations, you have one defensible record: the cluster has the history of decisions and status across teams\u2014plus the evidence trail back to customer conversations. So when a rep asks, \u201cIs this the same issue that\u2019s blocking Horizoncrest Systems\u2019 prospect in healthcare?\u201d you can answer with the cluster status, what teams have committed to, and consistent messaging.",
"link": "https://www.onyx.app/8135367-11"
},
{
"text": "Julian Morgan: How would Sales actually see this? They don\u2019t live in Zendesk or Jira.",
"link": "https://www.onyx.app/8135367-12"
},
{
"text": "Camila Vega: They shouldn\u2019t have to. Issue_Tracker can integrate with your CRM context and surface the relevant issue clusters back to revenue teams\u2014especially for active pipeline. Practically, that means Sales can search an account or opportunity and see: known issue clusters affecting similar customers, current engineering status, and a short \u201csafe to say\u201d blurb your team approves. The goal is fewer surprise escalations and fewer deals slipping because someone discovered an issue too late.",
"link": "https://www.onyx.app/8135367-13"
},
{
"text": "Julian Morgan: We\u2019ve had that happen\u2014an enterprise deal got stuck because we didn\u2019t realize the \u201cexport timeout\u201d problem was widespread until the prospect hit it. If we\u2019d known earlier, we could\u2019ve managed expectations.",
"link": "https://www.onyx.app/8135367-14"
},
{
"text": "Camila Vega: That\u2019s a perfect example. With clustering, \u201cexport timeout\u201d tickets across different wording\u2014\u201cdownload hangs,\u201d \u201cCSV export stalls,\u201d \u201creport export spinning\u201d\u2014get grouped into one theme. Then you can see trend volume rising, which segments are affected, and whether it\u2019s touching strategic accounts or late-stage deals. Even if Engineering is still investigating, Sales has a consistent status and can avoid overpromising.",
"link": "https://www.onyx.app/8135367-15"
},
{
"text": "Julian Morgan: What does implementation look like? We don\u2019t have appetite for a huge integration project right now.",
"link": "https://www.onyx.app/8135367-16"
},
{
"text": "Camila Vega: Understood. Typically the first step is lightweight: connect Zendesk and Jira, pull in recent history, and let Issue_Tracker generate initial clusters. Then you do a quick calibration session\u2014usually 60\u201390 minutes\u2014where you review top clusters, merge/split a few, and define a couple rules like \u201ctreat iOS and Android separately\u201d or \u201ckeep SSO issues distinct.\u201d After that, it runs continuously and your team just maintains the clusters that matter.",
"link": "https://www.onyx.app/8135367-17"
},
{
"text": "Julian Morgan: And who owns it? Support? Product Ops?",
"link": "https://www.onyx.app/8135367-18"
},
{
"text": "Camila Vega: It can vary, but the most successful setup is a shared workflow: Support Ops or Product Ops typically owns the system, but Sales benefits directly. The key is that everyone is looking at the same \u201cissue objects\u201d rather than each team maintaining separate tags and spreadsheets. If you have someone doing weekly triage or customer issue reviews, Issue_Tracker becomes the backbone for that.",
"link": "https://www.onyx.app/8135367-19"
},
{
"text": "Julian Morgan: This is resonating. I\u2019m curious how it would handle our product areas\u2014we have a platform layer plus a few modules, and tickets get misrouted a lot.",
"link": "https://www.onyx.app/8135367-20"
},
{
"text": "Camila Vega: That\u2019s a great fit for the configurable rules piece. You can cluster by product area and platform, and the system will still use semantic similarity within those boundaries. So you don\u2019t end up with a mega-cluster that mixes \u201cplatform auth\u201d with \u201cmodule reporting,\u201d even if customers describe them similarly. It reduces misrouting and makes the trends clearer for each owning team.",
"link": "https://www.onyx.app/8135367-21"
},
{
"text": "Julian Morgan: Makes sense. What would you need from us to see if this works with our data?",
"link": "https://www.onyx.app/8135367-22"
},
{
"text": "Camila Vega: For the next step, I\u2019d propose a full call where we do two things: (1) map your current flow\u2014Zendesk to Jira to internal comms\u2014and (2) look at a sample of your last 60\u201390 days of issues to see what clustering would produce. If you can bring someone from Support Ops and maybe a Product Ops counterpart, we can validate whether the top clusters match reality and identify quick wins\u2014like the top 10 themes impacting strategic accounts.",
"link": "https://www.onyx.app/8135367-23"
},
{
"text": "Julian Morgan: I can loop in our Support Ops lead. I\u2019d also like someone from RevOps, because deal risk is a big theme for us this quarter.",
"link": "https://www.onyx.app/8135367-24"
},
{
"text": "Camila Vega: Perfect\u2014RevOps is ideal, especially for tying issue clusters to pipeline risk and standardizing the messaging Sales uses. How\u2019s your schedule early next week? I have Tuesday at 11am or Wednesday at 2pm open for a 45-minute deeper dive.",
"link": "https://www.onyx.app/8135367-25"
},
{
"text": "Julian Morgan: Wednesday at 2pm works. Send an invite and I\u2019ll add Support Ops and RevOps.",
"link": "https://www.onyx.app/8135367-26"
},
{
"text": "Camila Vega: Great\u2014I\u2019ll send the calendar hold for Wednesday at 2pm with a short agenda and a couple prep questions\u2014mainly which support channel is primary and which Jira projects handle customer-facing work. Appreciate the time today, Julian.",
"link": "https://www.onyx.app/8135367-27"
},
{
"text": "Julian Morgan: Sounds good. Thanks, Camila\u2014looking forward to seeing it in action.",
"link": "https://www.onyx.app/8135367-28"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "camila_vega@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "julian_morgan@horizoncrest_systems.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_2683323",
"semantic_identifier": "2683323 - Blueharbor Dynamics - sales_call - Discussion with Nikhil Verma",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-01 00:00:00",
"metadata": {
"meeting_date": "2025-05-01 00:00:00",
"duration_min": "27"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "2683323 - Blueharbor Dynamics - sales_call - Discussion with Nikhil Verma",
"organizer_email": "camila_vega@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Camila Vega: Hi Nikhil\u2014good to see you again. Last time you mentioned Blueharbor Dynamics is trying to get sharper on which customer segments are actually worth building for, not just selling to. Does that still capture the priority for product planning?",
"link": "https://www.onyx.app/1712394-1"
},
{
"text": "Nikhil Verma: Yeah, that\u2019s right. We\u2019re getting pulled in a lot of directions. Sales has opinions, Product has opinions, and we don\u2019t have a clean way to decide which segment is truly high-fit versus just loud.",
"link": "https://www.onyx.app/1712394-2"
},
{
"text": "Camila Vega: Perfect. Today I want to keep this tight and focus on one piece of Customer_Analyzer that maps directly to that: the fit estimation. The idea is we ingest the signals you already have\u2014CRM fields, onboarding surveys, product usage events, support tickets, even interview notes\u2014and we turn that into a fit score from 0 to 100 at both the account and segment level. Then we show the driver breakdown so it\u2019s not a mystery.",
"link": "https://www.onyx.app/1712394-3"
},
{
"text": "Nikhil Verma: The \u201cnot a mystery\u201d part matters. My biggest concern is anything that feels like a black box. If it spits out a score but my team can\u2019t understand it, they won\u2019t use it.",
"link": "https://www.onyx.app/1712394-4"
},
{
"text": "Camila Vega: Totally fair, and honestly it\u2019s the most common concern on call two. What we do to avoid the black box perception is two things. First, you choose\u2014or we start from templates for\u2014your fit drivers. For example: industry and size, problem severity, time-to-value, adoption patterns, support burden, churn risk signals, willingness to pay. Second, every score comes with a driver breakdown: \u201cHere\u2019s why this account is an 82, and here\u2019s why this segment is a 61.\u201d It\u2019s basically a transparent model card for each output.",
"link": "https://www.onyx.app/1712394-5"
},
{
"text": "Nikhil Verma: Okay. But how do you connect it to outcomes? Like, I don\u2019t want a score that\u2019s just \u201cseems good.\u201d I want to see it tied to retention or expansion, otherwise it\u2019s just another dashboard.",
"link": "https://www.onyx.app/1712394-6"
},
{
"text": "Camila Vega: Exactly. The recommended ICP profile is based on observed outcomes you care about\u2014retention, conversion, expansion, support load. So if we find, for instance, that mid-market customers in a certain industry hit value in two weeks, adopt three key features, submit fewer tickets, and expand within 90 days, those drivers get weighted as \u201cwhat good looks like\u201d for your product today. And you can sanity-check it because you can click into the cohort and see examples of the accounts that make up that pattern.",
"link": "https://www.onyx.app/1712394-7"
},
{
"text": "Nikhil Verma: That would help. For product planning, we\u2019re trying to decide between building deeper workflow for Segment A versus adding integrations that would appeal to Segment B. We\u2019ve got anecdotes, but not proof.",
"link": "https://www.onyx.app/1712394-8"
},
{
"text": "Camila Vega: Great use case. Here\u2019s how teams typically use fit estimation for that exact decision: we compare segments side-by-side\u2014Segment A vs Segment B\u2014and Customer_Analyzer shows (1) the average fit score by segment, (2) the driver deltas, and (3) which unmet needs show up in your qualitative signals like interview notes or support themes. So you can say, \u201cSegment A is higher fit, but Segment B has an unmet need that\u2014if we solve it\u2014could raise fit and expansion.\u201d That\u2019s a roadmap bet grounded in data instead of opinions.",
"link": "https://www.onyx.app/1712394-9"
},
{
"text": "Nikhil Verma: Makes sense. Another concern: our data is messy. CRM fields aren\u2019t consistently filled out, and usage events are good but not perfect. How sensitive is the model to gaps?",
"link": "https://www.onyx.app/1712394-10"
},
{
"text": "Camila Vega: Also a fair callout. We handle it in a few ways: we can start with the signals that are strongest for you\u2014usually usage events and a few high-quality CRM fields\u2014and then layer in other sources as they\u2019re cleaned up. The fit score includes confidence indicators, so if an account has thin data, you\u2019ll see that. And the driver breakdown will show what it relied on. Practically, most customers start with \u201cgood enough\u201d data for an initial ICP direction, then refine.",
"link": "https://www.onyx.app/1712394-11"
},
{
"text": "Nikhil Verma: If it shows confidence, that helps. I just don\u2019t want Product making roadmap choices off shaky inputs without knowing it.",
"link": "https://www.onyx.app/1712394-12"
},
{
"text": "Camila Vega: Completely agree. Let me anchor this back to Blueharbor: based on what you shared last time, you have solid product usage telemetry and a decent ticketing system history. If we start with adoption patterns, time-to-value signals, and support burden as drivers, you\u2019ll quickly get a view of which segments succeed without needing perfect CRM hygiene on day one.",
"link": "https://www.onyx.app/1712394-13"
},
{
"text": "Nikhil Verma: Okay, that feels realistic. What would the first 30 days look like if we said yes?",
"link": "https://www.onyx.app/1712394-14"
},
{
"text": "Camila Vega: In the first week, we\u2019d connect your core sources\u2014typically CRM plus product events and support tickets. In week two, we\u2019d workshop fit drivers with you and one stakeholder\u2014still just a short session\u2014to define \u201cgood outcomes\u201d for the model to optimize against. Weeks three and four, you\u2019d get your initial segment comparison and recommended ICP profile, and we\u2019d validate it with a handful of known accounts: \u201cDo these high-fit accounts look like your best customers?\u201d Then you\u2019d use that output to guide the roadmap discussion you mentioned.",
"link": "https://www.onyx.app/1712394-15"
},
{
"text": "Nikhil Verma: I like the validation step. If we can pull up a few accounts and it matches what we know, I can get my team on board.",
"link": "https://www.onyx.app/1712394-16"
},
{
"text": "Camila Vega: Exactly. And if it doesn\u2019t match, we don\u2019t hand-wave it\u2014we adjust the drivers or the outcome target. That\u2019s how we keep trust high. Quick question: for product planning, which outcome matters more for you right now\u2014retention, expansion, or reducing support burden?",
"link": "https://www.onyx.app/1712394-17"
},
{
"text": "Nikhil Verma: Expansion and retention. Support burden matters, but we\u2019d rather grow with the right customers than optimize for fewer tickets.",
"link": "https://www.onyx.app/1712394-18"
},
{
"text": "Camila Vega: Great. Then we\u2019ll weight expansion and retention outcomes more heavily in the first model. That means the fit score will reflect \u201ccustomers like this convert, renew, and expand,\u201d not just \u201ccustomers like this are easy.\u201d",
"link": "https://www.onyx.app/1712394-19"
},
{
"text": "Nikhil Verma: Good. Pricing-wise\u2014are we talking something that\u2019s going to require a big procurement cycle, or can we start smaller?",
"link": "https://www.onyx.app/1712394-20"
},
{
"text": "Camila Vega: We can start smaller. Most teams begin with a single workspace focused on fit estimation for product planning\u2014segment scoring, driver breakdown, and ICP recommendation\u2014then expand to additional teams once it\u2019s proving value. For Blueharbor, I\u2019d propose a pilot sized to your top two segments plus your active customer base, so we can get statistically meaningful comparisons without boiling the ocean.",
"link": "https://www.onyx.app/1712394-21"
},
{
"text": "Nikhil Verma: And what do you need from me to make that pilot successful?",
"link": "https://www.onyx.app/1712394-22"
},
{
"text": "Camila Vega: Two things: (1) access to the core data sources\u2014read-only is fine\u2014and (2) agreement on the initial drivers and outcomes. Also, I\u2019d ask you to nominate 10 accounts you consider \u201cbest fit\u201d and 10 that churned or stalled. That gives us a fast reality check when we review results.",
"link": "https://www.onyx.app/1712394-23"
},
{
"text": "Nikhil Verma: That\u2019s doable. I can get you that list. I want to be sure the output is something we can actually take into a roadmap meeting. Is it like slides, or a dashboard, or\u2026?",
"link": "https://www.onyx.app/1712394-24"
},
{
"text": "Camila Vega: It\u2019s a dashboard you can export from. But more importantly, it translates into a decision artifact: \u201cHere are our top segments by fit. Here\u2019s why. Here\u2019s what high-fit customers do differently. Here are the drivers holding a segment back.\u201d Product teams typically paste the segment comparison and driver breakdown into their planning doc. If you want, we can also generate a one-page ICP summary.",
"link": "https://www.onyx.app/1712394-25"
},
{
"text": "Nikhil Verma: That\u2019s helpful. If we do this and it tells us Segment A is highest-fit, it also helps me push back on internal pressure to chase shiny segments.",
"link": "https://www.onyx.app/1712394-26"
},
{
"text": "Camila Vega: Exactly\u2014that alignment is one of the main outcomes. Let\u2019s get practical: would you be open to moving forward with a pilot that ends with a readout in four weeks, where success is \u201cwe can confidently pick a primary segment for the next roadmap cycle and explain why\u201d?",
"link": "https://www.onyx.app/1712394-27"
},
{
"text": "Nikhil Verma: Yes, that\u2019s the right bar. I\u2019d want a clear deliverable and not just \u201chere\u2019s a tool, good luck.\u201d",
"link": "https://www.onyx.app/1712394-28"
},
{
"text": "Camila Vega: Great. Next step: I\u2019ll send over a short pilot plan and a data connection checklist today. Can we pencil in a 45-minute kickoff early next week to define drivers and outcomes, and then a readout meeting four weeks after?",
"link": "https://www.onyx.app/1712394-29"
},
{
"text": "Nikhil Verma: Yes. Tuesday morning works for kickoff. Send the plan and I\u2019ll loop in our data lead for the access piece, but I\u2019ll stay the main owner.",
"link": "https://www.onyx.app/1712394-30"
},
{
"text": "Camila Vega: Perfect\u2014Tuesday morning it is. I\u2019ll send a calendar invite and the pilot outline within the hour. Anything else you want me to address before we lock it in?",
"link": "https://www.onyx.app/1712394-31"
},
{
"text": "Nikhil Verma: Just include something in the plan about transparency\u2014like how we\u2019ll review the driver breakdown and confidence so it doesn\u2019t feel like a black box.",
"link": "https://www.onyx.app/1712394-32"
},
{
"text": "Camila Vega: Absolutely. I\u2019ll make that a specific agenda item for the week-three check-in: \u201cscore rationale review + cohort spot-checking.\u201d Thanks, Nikhil\u2014excited to get Blueharbor to a clear ICP and a roadmap decision you can defend.",
"link": "https://www.onyx.app/1712394-33"
},
{
"text": "Nikhil Verma: Sounds good. Looking forward to it.",
"link": "https://www.onyx.app/1712394-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "camila_vega@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "nikhil_verma@blueharbor_dynamics.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_2921144",
"semantic_identifier": "2921144 - Ridgeway Lumen Telecom - sales_pitch - Discussion with Carl Burke",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-05 00:00:00",
"metadata": {
"meeting_date": "2025-05-05 00:00:00",
"duration_min": "32"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "2921144 - Ridgeway Lumen Telecom - sales_pitch - Discussion with Carl Burke",
"organizer_email": "irene_shen@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Irene Shen: Hi Carl\u2014this is Irene Shen. Thanks for taking a few minutes. I\u2019ll keep it tight. I\u2019m calling because we work with product and revenue teams to get crystal-clear on which customer segments are actually your best fit, and then turn that into messaging your team can use immediately in outbound and on landing pages.",
"link": "https://www.onyx.app/3097890-1"
},
{
"text": "Carl Burke: Hi Irene. Okay\u2014what are you seeing as the main gap you help with?",
"link": "https://www.onyx.app/3097890-2"
},
{
"text": "Irene Shen: Most teams have the data\u2014CRM fields, onboarding notes, support tickets, even sales call notes\u2014but it\u2019s scattered. Customer_Analyzer pulls those signals together and outputs two things: a fit score by segment so you can prioritize, and then positioning recommendations\u2014editable messaging blocks\u2014so marketing and sales aren\u2019t reinventing copy every week.",
"link": "https://www.onyx.app/3097890-3"
},
{
"text": "Carl Burke: \u201cPositioning recommendations\u201d can mean a lot. What does that actually look like?",
"link": "https://www.onyx.app/3097890-4"
},
{
"text": "Irene Shen: Totally fair. For a prioritized segment\u2014say SMB IT admins versus mid-market network ops\u2014Customer_Analyzer generates a segment-specific value proposition, top use cases, differentiators, proof points to collect, pricing/value framing notes, and an objection-handling playbook. Then it formats that into practical blocks: website hero copy, sales deck bullets, and email openers. And it links each line back to the drivers and evidence it used\u2014so it\u2019s not random AI copy.",
"link": "https://www.onyx.app/3097890-5"
},
{
"text": "Carl Burke: Interesting. We\u2019ve been trying to split messaging between SMB and mid-market, but it\u2019s messy. We keep arguing about what matters to each.",
"link": "https://www.onyx.app/3097890-6"
},
{
"text": "Irene Shen: That\u2019s exactly the use case I was going to ask about. For Ridgeway Lumen Telecom, do you already have two ICP tiers defined\u2014SMB vs mid-market\u2014or is that still in progress?",
"link": "https://www.onyx.app/3097890-7"
},
{
"text": "Carl Burke: We have a rough split, but it\u2019s not consistent. Sales calls anything \u201cmid-market\u201d if the deal is bigger, and marketing has one set of pages that tries to cover everyone.",
"link": "https://www.onyx.app/3097890-8"
},
{
"text": "Irene Shen: Makes sense. What Customer_Analyzer typically does is: first, we ingest the signals you already have\u2014your CRM fields, a few months of product usage events if you have them, support ticket tags, onboarding surveys, and any interview notes or call snippets you\u2019re willing to share. Then we score which segments are most likely to succeed with your product as it exists today\u2014so you\u2019re not targeting \u201cideal in theory.\u201d",
"link": "https://www.onyx.app/3097890-9"
},
{
"text": "Carl Burke: We do have CRM and support data. Product usage is in our analytics, but it\u2019s not well-labeled. How heavy is the setup?",
"link": "https://www.onyx.app/3097890-10"
},
{
"text": "Irene Shen: For a first pass, it\u2019s light. Most teams start with CRM + support + a handful of onboarding questions or notes. The goal in week one isn\u2019t perfect instrumentation\u2014it\u2019s getting directional clarity: \u201cWhich segments should we lean into, and what do they actually care about?\u201d Then you can refine with richer usage events later.",
"link": "https://www.onyx.app/3097890-11"
},
{
"text": "Carl Burke: Okay. But we\u2019ve had consultants do messaging workshops before. What\u2019s different besides it being software?",
"link": "https://www.onyx.app/3097890-12"
},
{
"text": "Irene Shen: Two big differences. One, it\u2019s continuous\u2014positioning stays tied to what customers are doing and saying, not just a one-time workshop. Two, it\u2019s operational. Instead of a slide deck that sits in a folder, you get editable messaging blocks your team can drop into a landing page, an SDR sequence, or a pitch deck\u2014plus the objection handling that sales actually needs. And because it\u2019s linked to evidence, it\u2019s easier to get internal alignment.",
"link": "https://www.onyx.app/3097890-13"
},
{
"text": "Carl Burke: The alignment part is real. Sales pushes for \u201cfaster install,\u201d product pushes \u201creliability,\u201d and marketing is stuck in the middle.",
"link": "https://www.onyx.app/3097890-14"
},
{
"text": "Irene Shen: Exactly. We\u2019ll surface what\u2019s truly winning\u2014by segment. For example, SMB might respond to \u201cfast setup and fewer tickets,\u201d while mid-market might care more about \u201cuptime, integrations, compliance,\u201d whatever the data shows for you. Then the system recommends how to frame value, what differentiators to lead with, what proof points to collect, and the most common objections\u2014like \u201cwe can build this ourselves\u201d or \u201cyour competitor is cheaper\u201d\u2014and how to answer them credibly.",
"link": "https://www.onyx.app/3097890-15"
},
{
"text": "Carl Burke: If it\u2019s generating website copy, how do we make sure it matches our voice and isn\u2019t generic?",
"link": "https://www.onyx.app/3097890-16"
},
{
"text": "Irene Shen: Good question. It learns from your own customer language\u2014support tickets, surveys, interview notes\u2014so it mirrors the phrases your customers already use. And everything is editable, so your team can tweak tone. The key is the structure and the segmentation: you\u2019re not starting from a blank page, and you\u2019re not writing one message for everyone.",
"link": "https://www.onyx.app/3097890-17"
},
{
"text": "Carl Burke: We\u2019re also running outbound. Could this help SDRs not spray the same email to every account?",
"link": "https://www.onyx.app/3097890-18"
},
{
"text": "Irene Shen: Yes. One of the fastest wins is segment-specific email openers and talk tracks. If you define two ICP tiers\u2014SMB vs mid-market\u2014you can generate two outbound angles with different proof points and objection handling. That typically improves reply rates and shortens discovery because you\u2019re leading with what matters to that segment.",
"link": "https://www.onyx.app/3097890-19"
},
{
"text": "Carl Burke: What do you need from us to show value quickly?",
"link": "https://www.onyx.app/3097890-20"
},
{
"text": "Irene Shen: For a focused pilot, I\u2019d ask for: a CRM export with a few key fields, a sample of recent closed-won and closed-lost notes if you have them, and a few months of support ticket categories. If you can add even a small set of onboarding survey answers or a couple of customer interview transcripts, that\u2019s a bonus. Then we\u2019d produce: (1) a fit score view for SMB vs mid-market, and (2) positioning recommendations for each\u2014landing page hero + bullets, plus outbound email openers and a basic objection playbook.",
"link": "https://www.onyx.app/3097890-21"
},
{
"text": "Carl Burke: That sounds doable. How long until we see those outputs?",
"link": "https://www.onyx.app/3097890-22"
},
{
"text": "Irene Shen: Typically within two weeks for the initial recommendations, depending on data access. And we can start with just the SMB vs mid-market split so it\u2019s not a huge taxonomy exercise.",
"link": "https://www.onyx.app/3097890-23"
},
{
"text": "Carl Burke: Who usually owns this internally\u2014marketing or product?",
"link": "https://www.onyx.app/3097890-24"
},
{
"text": "Irene Shen: Usually marketing or product marketing partners with sales leadership. But the benefit lands across teams: marketing gets segment landing page messaging, sales gets objection handling and talk tracks, and product gets clarity on what\u2019s driving retention and expansion in your best-fit segments.",
"link": "https://www.onyx.app/3097890-25"
},
{
"text": "Carl Burke: If we did this, I\u2019d want our head of demand gen and a sales manager in the room.",
"link": "https://www.onyx.app/3097890-26"
},
{
"text": "Irene Shen: Perfect. Here\u2019s what I propose: let\u2019s schedule a 30-minute deeper call this week. I\u2019ll walk through what the positioning recommendation output looks like\u2014value prop, differentiators, proof points, objection handling\u2014and we\u2019ll map it directly to your SMB vs mid-market landing page and outbound needs. If it looks relevant, we can outline a small pilot and the exact data we\u2019d pull.",
"link": "https://www.onyx.app/3097890-27"
},
{
"text": "Carl Burke: Yeah, I\u2019m interested. Thursday afternoon could work. Can you send an agenda so they know what they\u2019re joining?",
"link": "https://www.onyx.app/3097890-28"
},
{
"text": "Irene Shen: Absolutely. I\u2019ll send a calendar invite for Thursday afternoon with a tight agenda: your current SMB vs mid-market messaging challenge, the data sources we\u2019d use, and a live walkthrough of the messaging blocks\u2014landing page and outbound variants plus the objection playbook. Does 2:00 pm work?",
"link": "https://www.onyx.app/3097890-29"
},
{
"text": "Carl Burke: 2:00 pm works. Send it over and I\u2019ll pull in the right folks.",
"link": "https://www.onyx.app/3097890-30"
},
{
"text": "Irene Shen: Great\u2014I'll send that now. Thanks, Carl. Looking forward to it.",
"link": "https://www.onyx.app/3097890-31"
},
{
"text": "Carl Burke: Thanks, Irene. Talk Thursday.",
"link": "https://www.onyx.app/3097890-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "irene_shen@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "carl_burke@ridgeway_lumen_telecom.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_2965204",
"semantic_identifier": "2965204 - Xenith Argo Logistics - sales_pitch - Discussion with Claire Bell",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-03 00:00:00",
"metadata": {
"meeting_date": "2025-05-03 00:00:00",
"duration_min": "20"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "2965204 - Xenith Argo Logistics - sales_pitch - Discussion with Claire Bell",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi Claire\u2014Megan Foster here. Thanks for taking a few minutes. Did I catch you at an okay time?",
"link": "https://www.onyx.app/7242868-1"
},
{
"text": "Claire Bell: Hi Megan, yes\u2014I've got a few minutes. What\u2019s up?",
"link": "https://www.onyx.app/7242868-2"
},
{
"text": "Megan Foster: Perfect. I\u2019ll keep this quick. I\u2019m reaching out because we work with product and go-to-market teams that are trying to get really crisp on who their best customers are before they scale outbound or paid\u2014so they don\u2019t pour budget into the wrong segments. Does that sound like something Xenith Argo Logistics is wrestling with at all right now?",
"link": "https://www.onyx.app/7242868-3"
},
{
"text": "Claire Bell: Yeah, actually. We\u2019re debating which verticals to double down on. We\u2019ve got opinions, but it\u2019s not always grounded in data.",
"link": "https://www.onyx.app/7242868-4"
},
{
"text": "Megan Foster: That\u2019s exactly the moment our product, Customer_Analyzer, is built for. It pulls together signals you already have\u2014CRM fields, onboarding surveys, product usage events, support tickets, even interview notes\u2014and turns that into two useful outputs. The one I think you\u2019d care about most right now is our fit estimation: a fit score from 0 to 100 for accounts and segments, plus a breakdown of what\u2019s driving that score.",
"link": "https://www.onyx.app/7242868-5"
},
{
"text": "Claire Bell: Okay. How is that different from just looking at closed-won versus lost in the CRM?",
"link": "https://www.onyx.app/7242868-6"
},
{
"text": "Megan Foster: Great question. Closed-won/lost is a start, but it\u2019s often missing the \u201cwhy\u201d and it can be noisy\u2014sales cycle quirks, pricing exceptions, champion changes. Fit estimation looks at multiple drivers together and ties them to outcomes you care about: retention, expansion, time-to-value, support burden, and adoption patterns. So instead of \u201cwe win in this vertical,\u201d you get \u201cwe win and retain when these conditions are true,\u201d with a driver breakdown that shows what matters most.",
"link": "https://www.onyx.app/7242868-7"
},
{
"text": "Claire Bell: What kind of drivers are you talking about?",
"link": "https://www.onyx.app/7242868-8"
},
{
"text": "Megan Foster: You can define them or start from templates. Common ones are firmographics like industry and size, technographics if relevant, problem severity\u2014like how urgent the pain is\u2014willingness to pay signals, onboarding completion speed, adoption/usage patterns, and support and churn risk signals. The system then scores at the account level and aggregates to segment-level comparisons\u2014like SMB vs mid-market, or healthcare logistics vs manufacturing logistics\u2014so you can see where you\u2019re truly strong.",
"link": "https://www.onyx.app/7242868-9"
},
{
"text": "Claire Bell: We definitely see differences by segment, but it\u2019s hard to quantify. We also have a lot of support data that never really makes it into the \u201cICP\u201d discussion.",
"link": "https://www.onyx.app/7242868-10"
},
{
"text": "Megan Foster: Exactly\u2014and that support data is gold. One of the most common \u201cgotchas\u201d is a segment that buys quickly but is high support burden and churn-prone. Customer_Analyzer incorporates that, so you don\u2019t accidentally optimize for top-of-funnel conversion while creating churn later. In practice, teams use the fit score and the driver breakdown to validate or adjust their ICP before scaling acquisition.",
"link": "https://www.onyx.app/7242868-11"
},
{
"text": "Claire Bell: That\u2019s interesting. What would implementation look like? I don\u2019t want a six-month data project.",
"link": "https://www.onyx.app/7242868-12"
},
{
"text": "Megan Foster: Totally fair. The lightest-weight path is: connect your CRM, choose a few key usage and support signals, and map your outcomes\u2014things like renewal, expansion, onboarding success. Then we run an initial fit model and show you segment comparisons and what\u2019s driving the scores. Most teams can get to a first pass quickly because we\u2019re not asking you to instrument everything from scratch\u2014just start with what you already have and iterate.",
"link": "https://www.onyx.app/7242868-13"
},
{
"text": "Claire Bell: And it would tell us, say, \u201cthis type of customer is a better fit than that type\u201d?",
"link": "https://www.onyx.app/7242868-14"
},
{
"text": "Megan Foster: Exactly. You\u2019d see, for example, \u201cSegment A averages 78 fit with strong adoption and low support burden; Segment B averages 52 with slow time-to-value and higher churn signals,\u201d and then the driver breakdown explains what\u2019s behind it. We also produce a recommended ICP profile based on observed outcomes\u2014so it\u2019s not just a static persona, it\u2019s grounded in who actually succeeds with you.",
"link": "https://www.onyx.app/7242868-15"
},
{
"text": "Claire Bell: That would help. We\u2019ve been in a loop of debating ICP based on anecdotes from different teams.",
"link": "https://www.onyx.app/7242868-16"
},
{
"text": "Megan Foster: That\u2019s a common pattern. Customer_Analyzer creates a shared, defensible view so product, marketing, and sales are aligned. And just to keep this focused: the immediate use case I\u2019d propose for Xenith Argo Logistics is \u201cdefine and validate ICP before scaling paid acquisition\u201d\u2014so you can prioritize the right segments, improve conversion rates, and avoid churny cohorts.",
"link": "https://www.onyx.app/7242868-17"
},
{
"text": "Claire Bell: We are considering increasing spend next quarter, so the timing is good. How much effort do you need from our side to get started?",
"link": "https://www.onyx.app/7242868-18"
},
{
"text": "Megan Foster: For an initial evaluation, it\u2019s usually a small lift: someone who owns CRM access, someone who can point us to your usage events or product analytics, and optionally support\u2014like Zendesk exports or tags. Then we do a working session to pick the fit drivers that matter to you\u2014industry, size, onboarding completion, support burden, whatever is most predictive\u2014and we\u2019ll generate an initial fit score and segment comparison view.",
"link": "https://www.onyx.app/7242868-19"
},
{
"text": "Claire Bell: Would you need a lot of data science on our side?",
"link": "https://www.onyx.app/7242868-20"
},
{
"text": "Megan Foster: No. We handle the modeling and the analysis in the platform. Your team\u2019s role is mainly to define what \u201csuccess\u201d means for you and validate the drivers\u2014like \u201cthese tags indicate heavy support\u201d or \u201cthese events reflect adoption.\u201d We also make it very transparent with the driver breakdown, so it\u2019s not a black box.",
"link": "https://www.onyx.app/7242868-21"
},
{
"text": "Claire Bell: Okay. And what would I walk away with after the first full call?",
"link": "https://www.onyx.app/7242868-22"
},
{
"text": "Megan Foster: On the full call, we\u2019d aim to map your current segmentation and hypotheses, identify the signals you have available, and agree on the outcomes to optimize for. Then we\u2019ll show a sample of what the fit score and driver breakdown looks like, and outline a short path to an initial ICP recommendation\u2014something you can use immediately to guide targeting and messaging.",
"link": "https://www.onyx.app/7242868-23"
},
{
"text": "Claire Bell: That sounds useful. Who typically joins that call?",
"link": "https://www.onyx.app/7242868-24"
},
{
"text": "Megan Foster: Ideally you, plus whoever owns go-to-market strategy\u2014maybe someone in marketing or revenue ops\u2014and optionally product if they\u2019re part of ICP decisions. But we can keep it lean if that\u2019s easier: you and one other stakeholder is plenty for a first pass.",
"link": "https://www.onyx.app/7242868-25"
},
{
"text": "Claire Bell: I can bring our growth lead and maybe someone from RevOps. Can you send a couple of time options?",
"link": "https://www.onyx.app/7242868-26"
},
{
"text": "Megan Foster: Absolutely. Before I do\u2014one quick question to make the next conversation more relevant: what\u2019s the main debate right now\u2014industry vertical, company size, or something else?",
"link": "https://www.onyx.app/7242868-27"
},
{
"text": "Claire Bell: Mostly vertical and size. We do well with mid-market, but there\u2019s pressure to go downmarket. And we\u2019re not sure if that\u2019s smart.",
"link": "https://www.onyx.app/7242868-28"
},
{
"text": "Megan Foster: Got it. That\u2019s a perfect fit for segment comparisons\u2014mid-market versus SMB\u2014and we can incorporate time-to-value and support burden to see if downmarket actually pencils out. I\u2019ll email a couple time slots for a 30-minute working session and a short list of the data sources we\u2019d start with. Does early next week work?",
"link": "https://www.onyx.app/7242868-29"
},
{
"text": "Claire Bell: Yes, early next week is good.",
"link": "https://www.onyx.app/7242868-30"
},
{
"text": "Megan Foster: Great. I\u2019ll send options for Tuesday and Wednesday, and include a quick agenda: current ICP hypotheses, available signals, and what you want the fit score to optimize for. Thanks, Claire\u2014looking forward to it.",
"link": "https://www.onyx.app/7242868-31"
},
{
"text": "Claire Bell: Thanks, Megan. I\u2019ll keep an eye out for your email.",
"link": "https://www.onyx.app/7242868-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "claire_bell@xenith_argo_logistics.onyx.app"
}
]
}

View File

@@ -0,0 +1,157 @@
{
"id": "FIREFLIES_3846190",
"semantic_identifier": "3846190 - Driftwood Orion Co - sales_pitch - Discussion with Marcus Anderson",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-17 00:00:00",
"metadata": {
"meeting_date": "2025-05-17 00:00:00",
"duration_min": "44"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "3846190 - Driftwood Orion Co - sales_pitch - Discussion with Marcus Anderson",
"organizer_email": "camila_vega@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Camila Vega: Hi Marcus\u2014Camila Vega here. Thanks for taking a few minutes. I\u2019ll be quick: I wanted to share how Sales_Accelerator can help Driftwood Orion Co\u2019s SDR team turn your existing CRM data and your ICP work into \u201cwho do I call next and what do I say\u201d recommendations, without changing your CRM.",
"link": "https://www.onyx.app/6034222-1"
},
{
"text": "Marcus Anderson: Hey Camila. Sure, I\u2019ve got a few minutes. We\u2019re always looking at ways to improve outbound, but we\u2019re pretty set on Salesforce. What exactly is this doing?",
"link": "https://www.onyx.app/6034222-2"
},
{
"text": "Camila Vega: Perfect\u2014Sales_Accelerator sits on top of Salesforce, not instead of it. The specific piece I think you\u2019d care about is our sales initiation recommendations: it prioritizes the next-best accounts and then generates next-best-message openers for each account\u2014email, LinkedIn, even a call talk track\u2014based on your CRM signals and any fit/positioning data you already have.",
"link": "https://www.onyx.app/6034222-3"
},
{
"text": "Marcus Anderson: We have a lot of tooling that claims prioritization. The issue is reps don\u2019t trust the list, and messaging gets messy. How is this different?",
"link": "https://www.onyx.app/6034222-4"
},
{
"text": "Camila Vega: That\u2019s exactly the gap we built for. Two differences: first, explainability\u2014every recommendation is tied back to the rationale, like fit drivers, segment messaging blocks, and trigger events we detect. Second, alignment\u2014Sales_Accelerator can consume Customer_Analyzer outputs, so you\u2019re not reinventing ICP or positioning. If Customer_Analyzer says \u201cthis is a high-fit account in Segment B,\u201d we\u2019ll recommend an opener and proof points that match that segment\u2019s approved positioning, plus objection handlers your team wants used.",
"link": "https://www.onyx.app/6034222-5"
},
{
"text": "Marcus Anderson: Okay. But practically, how does that show up for an SDR? Because we\u2019ve tried \u201cbattlecards\u201d and \u201cplaybooks,\u201d and adoption fizzles.",
"link": "https://www.onyx.app/6034222-6"
},
{
"text": "Camila Vega: Totally fair. Practically: an SDR opens their \u201cNext 50\u201d list for the day. It\u2019s not a static tier list\u2014it\u2019s ranked based on fit + recent engagement/intent + CRM activity. When they click an account, they get a ready-to-send first-touch email draft, a LinkedIn connection note, and a call opener\u2014each one tailored to the segment. And it\u2019s not just copy: it includes the meeting agenda suggestion and which proof points to use. The SDR can edit, but they\u2019re starting from something that\u2019s on-message and consistent.",
"link": "https://www.onyx.app/6034222-7"
},
{
"text": "Marcus Anderson: The \u201cNext 50\u201d concept is interesting. We struggle with reps cherry-picking accounts or working the loudest ones, not the best ones.",
"link": "https://www.onyx.app/6034222-8"
},
{
"text": "Camila Vega: Exactly. One of the common use cases is an SDR team prioritizing outbound by pulling Customer_Analyzer fit tiers into Sales_Accelerator, and then we recommend the next 50 accounts to contact each day\u2014so you\u2019re consistently hitting your best-fit accounts first. Managers can also set guardrails like \u201c80% of touches must be in Tier 1 and Tier 2,\u201d or \u201cthis segment gets this sequence,\u201d so it\u2019s enforceable without being heavy-handed.",
"link": "https://www.onyx.app/6034222-9"
},
{
"text": "Marcus Anderson: What inputs do you need from us to make that work? I don\u2019t want a six-month implementation.",
"link": "https://www.onyx.app/6034222-10"
},
{
"text": "Camila Vega: You shouldn\u2019t have to. Minimum inputs are your Salesforce account and activity data plus whatever engagement signals you already track\u2014email opens, web visits, intent feeds if you have them. If you\u2019re already using Customer_Analyzer, we simply ingest the fit scores and segment positioning blocks. Then we configure a couple initiation playbooks\u2014like \u201cTier 1 inbound follow-up\u201d and \u201cTier 1 outbound cold\u201d\u2014and you\u2019re live for a pilot quickly.",
"link": "https://www.onyx.app/6034222-11"
},
{
"text": "Marcus Anderson: We\u2019re not using Customer_Analyzer today. Does that break the value?",
"link": "https://www.onyx.app/6034222-12"
},
{
"text": "Camila Vega: Not at all. Customer_Analyzer just makes the ICP/positioning alignment cleaner. Without it, we can still rank accounts using your CRM and engagement signals and generate initiation playbooks. But if you do have an internal fit model or even a simple tiering list, we can ingest that too, so your team still gets consistent targeting and segment-based messaging.",
"link": "https://www.onyx.app/6034222-13"
},
{
"text": "Marcus Anderson: Got it. How do you prevent the messages from sounding generic? SDRs hate templates that feel like everyone else.",
"link": "https://www.onyx.app/6034222-14"
},
{
"text": "Camila Vega: We\u2019re not trying to spit out \u201cone template for all.\u201d The recommendations are structured: a suggested angle, 2\u20133 proof points, and a couple objection handlers mapped to the segment. Then we generate a draft that pulls those elements together. The SDR can personalize the first line or swap a proof point, but the core is anchored to what works for that segment\u2014so it\u2019s personalized without going off-brand or improvising weak positioning.",
"link": "https://www.onyx.app/6034222-15"
},
{
"text": "Marcus Anderson: And what\u2019s the measurable impact you typically see? If I bring this to leadership, they\u2019ll ask for hard outcomes.",
"link": "https://www.onyx.app/6034222-16"
},
{
"text": "Camila Vega: The most direct metrics are reply rates and meeting creation, plus a reduction in wasted prospecting time. Because reps aren\u2019t spending the first hour figuring out \u201cwho\u2019s next\u201d and \u201cwhat do I say,\u201d they get more quality touches out. Also, managers get cleaner activity distribution\u2014more touches concentrated on your best-fit accounts instead of random walk targeting.",
"link": "https://www.onyx.app/6034222-17"
},
{
"text": "Marcus Anderson: This sounds like it would help our newer SDRs a lot. The tenured folks might resist being told what to do.",
"link": "https://www.onyx.app/6034222-18"
},
{
"text": "Camila Vega: That\u2019s a common dynamic. We position it as guidance, not handcuffs. Tenured reps can still override, but the system makes it obvious when they\u2019re deviating\u2014and why. What we usually see is the experienced SDRs use it as a time-saver for drafting and sequencing, while newer SDRs use it as training wheels to learn the right angles and proof points faster.",
"link": "https://www.onyx.app/6034222-19"
},
{
"text": "Marcus Anderson: How does it fit into a normal day? Are they living in another tool?",
"link": "https://www.onyx.app/6034222-20"
},
{
"text": "Camila Vega: They can stay in Salesforce for the core workflow. Sales_Accelerator surfaces the prioritized list and the initiation playbook suggestions in the context of the account. The goal is minimal tab-switching: \u201cHere\u2019s the next account, here\u2019s the recommended angle, here\u2019s the email/LinkedIn/call opener, here\u2019s the reason.\u201d Managers can review adoption and outcomes without pulling spreadsheets.",
"link": "https://www.onyx.app/6034222-21"
},
{
"text": "Marcus Anderson: If we wanted to evaluate it, what would a pilot look like?",
"link": "https://www.onyx.app/6034222-22"
},
{
"text": "Camila Vega: I\u2019d propose a short pilot with one SDR pod\u2014say 6 to 10 reps\u2014focused on outbound for your top segments. We\u2019d define the \u201cNext 50\u201d rules, build two initiation playbooks (one cold outbound, one inbound follow-up), and track meeting creation and reply rate versus a baseline. The key is we\u2019ll keep it simple: a couple segments, a couple playbooks, and very clear success criteria.",
"link": "https://www.onyx.app/6034222-23"
},
{
"text": "Marcus Anderson: That\u2019s reasonable. I\u2019d want our sales ops lead involved, because they\u2019ll ask about data access and governance.",
"link": "https://www.onyx.app/6034222-24"
},
{
"text": "Camila Vega: Absolutely. Why don\u2019t we do a 45-minute working session next week with you and sales ops? I can walk through the Salesforce integration, show what the SDR sees for the next-best-account and next-best-message flow, and we can map it to your current outreach process. If it looks like a fit, we\u2019ll outline a pilot plan on that call.",
"link": "https://www.onyx.app/6034222-25"
},
{
"text": "Marcus Anderson: Yeah, let\u2019s do it. Send me a couple times, and I\u2019ll pull in sales ops. Tuesday or Wednesday would work.",
"link": "https://www.onyx.app/6034222-26"
},
{
"text": "Camila Vega: Perfect. I\u2019ll email you two options for Tuesday and Wednesday, plus a short agenda: current outbound motion, how you prioritize today, segment messaging inputs, and then a demo of the initiation recommendations with the \u201cNext 50\u201d view. Sound good?",
"link": "https://www.onyx.app/6034222-27"
},
{
"text": "Marcus Anderson: Sounds good. Looking forward to seeing it.",
"link": "https://www.onyx.app/6034222-28"
},
{
"text": "Camila Vega: Great\u2014thanks Marcus. I\u2019ll send that shortly and we\u2019ll take it from there.",
"link": "https://www.onyx.app/6034222-29"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "camila_vega@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "marcus_anderson@driftwood_orion_co.onyx.app"
}
]
}

View File

@@ -0,0 +1,201 @@
{
"id": "FIREFLIES_3996862",
"semantic_identifier": "3996862 - Valeforge Automotive - sales_call - Discussion with Parker Chapman",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-09 00:00:00",
"metadata": {
"meeting_date": "2025-05-09 00:00:00",
"duration_min": "25"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "3996862 - Valeforge Automotive - sales_call - Discussion with Parker Chapman",
"organizer_email": "irene_shen@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Irene Shen: Hi Parker\u2014good to see you again. Thanks for making time for a quick follow-up. Last time you mentioned you\u2019re trying to decide which industries to lean into next quarter, especially balancing retention with support burden. Still the top priority?",
"link": "https://www.onyx.app/2079667-1"
},
{
"text": "Parker Chapman: Hey Irene, yeah, that\u2019s still it. We\u2019ve got a couple industries that look promising on revenue, but support tickets and onboarding time can get ugly. And internally we\u2019re not aligned\u2014Sales wants one direction, Marketing another, Product is focused on a different segment entirely.",
"link": "https://www.onyx.app/2079667-2"
},
{
"text": "Irene Shen: That\u2019s helpful context. For today, I\u2019d like to keep this focused on one part of Customer_Analyzer that maps directly to that\u2014our positioning recommendations for prioritized segments. The goal is to get you to a decision-ready view: \u201cThese are the segments we should target, and here\u2019s the messaging we\u2019ll all use consistently.\u201d Does that sound like the right outcome for this call?",
"link": "https://www.onyx.app/2079667-3"
},
{
"text": "Parker Chapman: Yes. The \u201cconsistent\u201d part is the hard one. We\u2019ve had too many circular debates.",
"link": "https://www.onyx.app/2079667-4"
},
{
"text": "Irene Shen: Totally. Here\u2019s how it typically works with teams like yours: you already have signals scattered across systems\u2014CRM fields, onboarding surveys, product usage, support tickets, even interview notes. Customer_Analyzer ingests those and translates them into positioning recommendations, but importantly, each recommendation is linked back to the evidence and fit drivers so it\u2019s not just \u201copinion.\u201d",
"link": "https://www.onyx.app/2079667-5"
},
{
"text": "Parker Chapman: When you say \u201cpositioning recommendations,\u201d what exactly do we get? Because we\u2019ve tried personas and messaging docs and they just sit in a folder.",
"link": "https://www.onyx.app/2079667-6"
},
{
"text": "Irene Shen: Good question. The output is a set of editable messaging blocks per segment\u2014things you can actually ship. For example: a segment-specific value proposition, top use cases, key differentiators, proof points to collect, pricing/value framing notes, and an objection-handling playbook. And then we format those as website hero copy, sales deck bullets, and even email openers\u2014so Marketing and Sales can lift and use them without rewriting from scratch.",
"link": "https://www.onyx.app/2079667-7"
},
{
"text": "Parker Chapman: Okay. My concern is that our segments aren\u2019t clean. We have \u201cfleet management,\u201d \u201cparts distributors,\u201d \u201cservice centers,\u201d and then there are hybrids. Sales reps label things differently in the CRM too.",
"link": "https://www.onyx.app/2079667-8"
},
{
"text": "Irene Shen: That\u2019s very common, especially in automotive. Two things we\u2019d do in the first pass: one, normalize what you already have\u2014so if reps are using different labels, we map those to a consistent taxonomy. Two, we don\u2019t rely on a single field. We use multiple signals\u2014like retention by segment, support ticket volume and themes, onboarding time, and product usage patterns\u2014to see what segments actually behave differently in ways that matter.",
"link": "https://www.onyx.app/2079667-9"
},
{
"text": "Parker Chapman: That would help. But we also have the politics problem. Marketing tends to push broad messaging. Sales wants super specific, and Product wants to keep it aligned to the roadmap. How does your tool stop the arguments?",
"link": "https://www.onyx.app/2079667-10"
},
{
"text": "Irene Shen: It won\u2019t eliminate opinions, but it changes the structure of the conversation. Instead of \u201cI feel like fleet is better,\u201d it becomes: \u201cFleet has high retention but high support burden driven by these ticket themes; service centers have lower retention but low support and faster time-to-value; here\u2019s the language customers use when they\u2019re happy; here are the objections we keep hearing.\u201d The positioning recommendations are literally tied back to those drivers and evidence\u2014so stakeholders can debate the inputs and priorities, not argue over wording in isolation.",
"link": "https://www.onyx.app/2079667-11"
},
{
"text": "Parker Chapman: I like the idea. But I\u2019m skeptical about \u201crecommended messaging.\u201d Our industry has nuance. I don\u2019t want generic SaaS copy.",
"link": "https://www.onyx.app/2079667-12"
},
{
"text": "Irene Shen: Completely fair. The recommendation engine is not trying to write clever taglines. It pulls observed customer language from the signals you provide\u2014support tickets, survey responses, call notes\u2014and then suggests messaging blocks that reflect how customers talk about value. And everything is editable. The key is speed and consistency: you get a strong baseline quickly, and then your team tweaks it, but you\u2019re tweaking from the same source of truth.",
"link": "https://www.onyx.app/2079667-13"
},
{
"text": "Parker Chapman: How quickly is \u201cquickly\u201d?",
"link": "https://www.onyx.app/2079667-14"
},
{
"text": "Irene Shen: For a first usable set of outputs, most teams get there in about two weeks after data connections\u2014depending on how fast you can give access to the key sources. In week one, we ingest and normalize. In week two, we produce segment recommendations and review them with you. By the end of that, you typically have a short list of target industries and a messaging pack Sales and Marketing can both adopt.",
"link": "https://www.onyx.app/2079667-15"
},
{
"text": "Parker Chapman: Two weeks is reasonable. What data do you need from us specifically to do the \u201cretention vs support burden\u201d prioritization?",
"link": "https://www.onyx.app/2079667-16"
},
{
"text": "Irene Shen: Minimum viable: a CRM export with account industry, ARR, churn/renewal status, and any existing segment labels; support ticket data with ticket volume and tags or categories; and ideally onboarding survey responses or time-to-value markers if you track them. If product usage events are available, even better, but we can start without them.",
"link": "https://www.onyx.app/2079667-17"
},
{
"text": "Parker Chapman: We can do CRM and support. Usage data is more work\u2014our data team is slammed.",
"link": "https://www.onyx.app/2079667-18"
},
{
"text": "Irene Shen: That\u2019s fine for phase one. We can still build solid positioning recommendations using retention and support burden signals, which is your stated use case. Then later we can layer usage events to refine fit drivers and sharpen objections and proof points.",
"link": "https://www.onyx.app/2079667-19"
},
{
"text": "Parker Chapman: Another worry: if the tool spits out recommendations that contradict what our Sales leaders believe, it could create more friction.",
"link": "https://www.onyx.app/2079667-20"
},
{
"text": "Irene Shen: That can happen\u2014and it\u2019s actually why the evidence links matter. When there\u2019s a conflict, we can show: \u201cHere\u2019s the support burden by segment. Here are the churn reasons in notes. Here\u2019s the language customers use in tickets and surveys.\u201d It doesn\u2019t force a decision, but it makes the tradeoffs explicit. And we can also present two positioning options if you want: one optimized for win-rate, another optimized for retention/support load, so leadership can choose based on strategy.",
"link": "https://www.onyx.app/2079667-21"
},
{
"text": "Parker Chapman: That\u2019s a good framing. We\u2019ve been mixing goals\u2014Sales is chasing logos and Product is trying to protect support capacity.",
"link": "https://www.onyx.app/2079667-22"
},
{
"text": "Irene Shen: Exactly. So if we anchor the next-quarter plan on your constraints\u2014retention and support burden\u2014and then produce segment-specific value props and an objection playbook, you\u2019ll reduce thrash. Sales can still go after deals, but with clearer qualification and better expectation-setting.",
"link": "https://www.onyx.app/2079667-23"
},
{
"text": "Parker Chapman: The objection playbook sounds useful. We get the same pushback over integrations and implementation time, and reps handle it inconsistently.",
"link": "https://www.onyx.app/2079667-24"
},
{
"text": "Irene Shen: That\u2019s a perfect fit. For each prioritized segment, we\u2019ll outline the top objections we see in your data and recommended responses\u2014plus proof points to collect. For example, if \u201cimplementation time\u201d is a recurring objection in service centers, we\u2019d recommend a specific proof point like \u201cgo-live in X days\u201d and suggest how to position it relative to what they care about\u2014reduced downtime, faster technician throughput, things like that.",
"link": "https://www.onyx.app/2079667-25"
},
{
"text": "Parker Chapman: Okay. If we did this, how would you propose we run it internally so it actually gets adopted? That\u2019s my biggest fear\u2014good output, no behavior change.",
"link": "https://www.onyx.app/2079667-26"
},
{
"text": "Irene Shen: I\u2019d suggest a tight pilot with a clear decision: pick two or three target industries you\u2019re considering for next quarter. We generate positioning recommendations and messaging blocks for each. Then you run a single working session with you plus one leader each from Sales, Marketing, and Product\u2014where the tool\u2019s evidence is on screen. The deliverable isn\u2019t a \u201cdocument,\u201d it\u2019s: \u201cThese two industries are in, this one is out; here\u2019s the approved value prop and differentiators; here are the objections and proof points we\u2019ll standardize.\u201d Adoption tends to stick when there\u2019s an explicit approval moment.",
"link": "https://www.onyx.app/2079667-27"
},
{
"text": "Parker Chapman: That\u2019s realistic. I can get the right people in a room if there\u2019s a concrete output.",
"link": "https://www.onyx.app/2079667-28"
},
{
"text": "Irene Shen: Great. To move this toward buying, the next step I\u2019d propose is a 30-day pilot scoped to your use case: prioritize industries using retention + support signals, then deliver segment-specific positioning recommendations\u2014value prop, differentiators, proof points, and objection handling\u2014packaged as editable blocks for your website and sales collateral. If we agree on success criteria upfront, it\u2019s very measurable.",
"link": "https://www.onyx.app/2079667-29"
},
{
"text": "Parker Chapman: What would success criteria look like?",
"link": "https://www.onyx.app/2079667-30"
},
{
"text": "Irene Shen: Something like: by day 30, you have (1) a ranked list of target industries with the drivers clearly documented; (2) approved messaging blocks for the top two segments; and (3) an objection-handling playbook that Sales leadership signs off on. Optionally, you could test the email openers with a small outbound campaign, but that\u2019s not required to prove value.",
"link": "https://www.onyx.app/2079667-31"
},
{
"text": "Parker Chapman: That sounds reasonable. What about pricing for the pilot and what you need from us to start?",
"link": "https://www.onyx.app/2079667-32"
},
{
"text": "Irene Shen: Pricing depends on seat count and data sources, but for a pilot like this we typically keep it simple: core access for you and a small group of stakeholders, plus onboarding support. If you\u2019re open to it, I can send a pilot proposal today with the exact scope, timeline, and success metrics, and we can review it together early next week. To start, we\u2019d need CRM and support ticket access or exports, and one point person on your side for data coordination.",
"link": "https://www.onyx.app/2079667-33"
},
{
"text": "Parker Chapman: Send the proposal. I can be the point person, and I can get you a CRM export this week. Support data might take a few days but it\u2019s doable.",
"link": "https://www.onyx.app/2079667-34"
},
{
"text": "Irene Shen: Perfect. Last quick check: given the internal alignment issue you mentioned, should we bake that stakeholder working session into the pilot plan so it\u2019s not optional?",
"link": "https://www.onyx.app/2079667-35"
},
{
"text": "Parker Chapman: Yes\u2014please do. If it\u2019s not on the calendar, it won\u2019t happen.",
"link": "https://www.onyx.app/2079667-36"
},
{
"text": "Irene Shen: Done. I\u2019ll include a proposed agenda: review segment tradeoffs, approve the top two industries, finalize value prop and differentiators, and align on the objection playbook. I\u2019ll send the proposal EOD, and let\u2019s tentatively hold Tuesday or Wednesday for a 20-minute review\u2014what works better?",
"link": "https://www.onyx.app/2079667-37"
},
{
"text": "Parker Chapman: Wednesday morning works.",
"link": "https://www.onyx.app/2079667-38"
},
{
"text": "Irene Shen: Great\u2014Wednesday morning it is. I\u2019ll send the proposal and a calendar hold. Thanks, Parker. This feels like a good fit for what Valeforge is trying to do next quarter.",
"link": "https://www.onyx.app/2079667-39"
},
{
"text": "Parker Chapman: Thanks Irene. This is the most concrete approach we\u2019ve discussed so far. Looking forward to the proposal.",
"link": "https://www.onyx.app/2079667-40"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "irene_shen@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "parker_chapman@valeforge_automotive.onyx.app"
}
]
}

View File

@@ -0,0 +1,193 @@
{
"id": "FIREFLIES_5969428",
"semantic_identifier": "5969428 - Prairielark Education - sales_call - Discussion with David Green",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-16 00:00:00",
"metadata": {
"meeting_date": "2025-05-16 00:00:00",
"duration_min": "34"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "5969428 - Prairielark Education - sales_call - Discussion with David Green",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi David, good to see you again. Last time you mentioned you\u2019re getting hit with repeat \u201csame problem\u201d tickets from different districts and it\u2019s hard to tell what\u2019s actually widespread versus just noisy. I wanted to focus today on Issue_Tracker\u2014specifically the issue clustering piece\u2014and how it ties back to protecting late-stage deals.",
"link": "https://www.onyx.app/3310916-1"
},
{
"text": "David Green: Hey Megan. Yeah, that\u2019s still the pain. Support says it\u2019s everywhere, product says it\u2019s isolated, and sales is stuck in the middle trying to explain what\u2019s going on to prospects.",
"link": "https://www.onyx.app/3310916-2"
},
{
"text": "Megan Foster: Exactly. So Issue_Tracker sits between Zendesk or Intercom and your internal tools like Jira, and it standardizes an \u201cissue object\u201d\u2014a theme with evidence, impacted customers, and linked source conversations. The most immediate win is issue_clustering: it automatically groups related tickets, chats, and call-note summaries into deduped clusters, so you stop triaging the same thing ten times under ten different labels.",
"link": "https://www.onyx.app/3310916-3"
},
{
"text": "David Green: Okay. We already tag tickets and we have macros. The problem is we don\u2019t trust the tags, and we still end up with spreadsheets when leadership asks \u201chow big is this?\u201d",
"link": "https://www.onyx.app/3310916-4"
},
{
"text": "Megan Foster: That\u2019s a common pattern. Clustering gives you a defensible view: the cluster shows top phrases, representative examples, suggested naming, and it keeps the links back to each Zendesk ticket or Intercom thread. If someone questions \u201cis this real,\u201d you can open the cluster and see the underlying evidence\u2014who said it, when, and from which channel.",
"link": "https://www.onyx.app/3310916-5"
},
{
"text": "David Green: How much manual effort are we talking? Because if this becomes another tool that needs constant grooming, it\u2019ll die.",
"link": "https://www.onyx.app/3310916-6"
},
{
"text": "Megan Foster: Fair. The default workflow is mostly automated, but with guardrails: it proposes merges and splits, and you can set rules by product area, platform, severity keywords\u2014so you\u2019re not mixing \u201cmobile login\u201d with \u201cSIS integration,\u201d for example. Usually a support lead or ops person spends a short weekly pass reviewing the clusters that are ambiguous; the rest runs on autopilot.",
"link": "https://www.onyx.app/3310916-7"
},
{
"text": "David Green: The weekly pass sounds like \u201csomeone\u2019s job now.\u201d Also, our org is sensitive to anything that looks like an algorithm telling them what\u2019s important. That came up with a previous vendor.",
"link": "https://www.onyx.app/3310916-8"
},
{
"text": "Megan Foster: I want to separate two things. Clustering is about deduping and making volume visible\u2014less about telling you what to do. The prioritization layer can be configured, but we don\u2019t need to lead with that today. For Prairielark, the use case you flagged was deal risk: sales wants a clean answer when a late-stage prospect hits a known issue. That\u2019s where linking an opportunity to an issue cluster helps\u2014sales sees current status, internal notes, and safe-to-say language, instead of chasing product in Slack.",
"link": "https://www.onyx.app/3310916-9"
},
{
"text": "David Green: Linking opportunities sounds great in theory. In practice, we\u2019ve been burned. People argue about severity versus frequency. And if the score is a black box, product will ignore it and create their own list.",
"link": "https://www.onyx.app/3310916-10"
},
{
"text": "Megan Foster: Totally. When teams feel it\u2019s a black box, they revert to parallel spreadsheets. What we do is make the weighting explicit: you can show the components\u2014frequency, affected segment, revenue exposure\u2014so if leadership wants to adjust ARR weighting for enterprise districts, you do it transparently.",
"link": "https://www.onyx.app/3310916-11"
},
{
"text": "David Green: But even if it\u2019s \u201ctransparent,\u201d it still becomes a political debate every quarter. And right now we\u2019re trying to reduce process overhead, not add another forum where support and product argue.",
"link": "https://www.onyx.app/3310916-12"
},
{
"text": "Megan Foster: Understood. If the goal is fewer debates, one approach is to treat Issue_Tracker as the single source of evidence, not the single source of priority. So: clustering produces the facts\u2014what\u2019s being reported, by whom, how often, what it\u2019s tied to\u2014and your existing process decides priority. Then later, if you want, you can layer in impact scoring.",
"link": "https://www.onyx.app/3310916-13"
},
{
"text": "David Green: That might be more acceptable. But I\u2019m still worried about accuracy. Education customers describe the same issue in wildly different terms. If the clustering is off, it\u2019ll either undercount or lump unrelated stuff together.",
"link": "https://www.onyx.app/3310916-14"
},
{
"text": "Megan Foster: That\u2019s exactly why we use semantic similarity instead of pure keyword matching, and why there\u2019s a merge/split workflow. If two clusters are actually the same root cause\u2014say \u201cgrade passback failing\u201d and \u201cSIS sync error\u201d\u2014the system will suggest a merge based on overlapping phrases and linked components, but your team approves it. And if it lumps unrelated issues, you split it and the model learns from those decisions.",
"link": "https://www.onyx.app/3310916-15"
},
{
"text": "David Green: You\u2019re saying \u201cthe model learns,\u201d but we\u2019ve heard that before. What happens when it\u2019s wrong for months and we make decisions on bad groupings?",
"link": "https://www.onyx.app/3310916-16"
},
{
"text": "Megan Foster: Practically, we recommend starting with a narrow scope. For example: pick one product area\u2014maybe SIS integrations since that\u2019s where districts get loud\u2014and run clustering only there for 30 days. You validate: do the clusters match what your support lead already knows? Are you seeing duplicates collapse? If it\u2019s not accurate enough, you don\u2019t roll it out broadly.",
"link": "https://www.onyx.app/3310916-17"
},
{
"text": "David Green: That pilot sounds fine. But procurement is asking us to consolidate tooling, not add. We already have Zendesk, Jira, Salesforce, and a couple analytics tools. Another layer is a hard sell internally.",
"link": "https://www.onyx.app/3310916-18"
},
{
"text": "Megan Foster: I hear you. The argument isn\u2019t \u201canother tool,\u201d it\u2019s reducing the hidden tool\u2014spreadsheets, Slack threads, and ad hoc triage meetings. Issue_Tracker becomes the connective tissue: Zendesk stays Zendesk, Jira stays Jira, but the \u201cissue object\u201d and its history stop being scattered.",
"link": "https://www.onyx.app/3310916-19"
},
{
"text": "David Green: Our leadership will ask: why can\u2019t Zendesk do this? Or why can\u2019t we just standardize Jira tickets better?",
"link": "https://www.onyx.app/3310916-20"
},
{
"text": "Megan Foster: Zendesk is great for individual tickets, but it doesn\u2019t create a persistent cluster with business context and links to downstream engineering work in a standardized way. Jira is great for delivery tasks, but it\u2019s not where you want to store customer evidence and the \u201cwhat are customers saying across channels\u201d picture. Issue_Tracker is the layer in between.",
"link": "https://www.onyx.app/3310916-21"
},
{
"text": "David Green: Okay, but bringing this back to sales\u2014our sales team wants something simple. \u201cIs this fixed? What do I tell the district?\u201d If they have to learn a new system or interpret clusters, they won\u2019t use it.",
"link": "https://www.onyx.app/3310916-22"
},
{
"text": "Megan Foster: That\u2019s why the workflow is: sales doesn\u2019t manage clusters. They search the issue by phrase, or it gets linked automatically when an opportunity mentions a known theme. Then they see: status, ETA notes if available, and approved language that product/support agreed is safe to communicate. It\u2019s less interpretation, more \u201chere\u2019s the current truth.\u201d",
"link": "https://www.onyx.app/3310916-23"
},
{
"text": "David Green: And who writes the \u201capproved language\u201d? Because that becomes another bottleneck and another debate.",
"link": "https://www.onyx.app/3310916-24"
},
{
"text": "Megan Foster: Typically product or support ops drafts it once per cluster, and it\u2019s versioned. But you can keep it lightweight\u2014two sentences: what\u2019s happening and what the workaround is. The point is preventing each AE from inventing their own narrative.",
"link": "https://www.onyx.app/3310916-25"
},
{
"text": "David Green: This is all reasonable, but I\u2019m not convinced it won\u2019t turn into a governance project. We\u2019re stretched. Also, our data is messy\u2014ticket fields aren\u2019t consistent, and sometimes issues come in through email that never makes it into Zendesk.",
"link": "https://www.onyx.app/3310916-26"
},
{
"text": "Megan Foster: If inputs aren\u2019t centralized, clustering can\u2019t see them. We can ingest emails and call-note summaries too, but yes\u2014garbage in, garbage out. We\u2019d need to confirm your channel coverage. If a lot of high-severity issues bypass Zendesk, the clusters will be incomplete.",
"link": "https://www.onyx.app/3310916-27"
},
{
"text": "David Green: That\u2019s the reality. And honestly, we\u2019re mid-migration on support workflows. I\u2019m hesitant to layer something on top until that stabilizes.",
"link": "https://www.onyx.app/3310916-28"
},
{
"text": "Megan Foster: That makes sense. Given that, would it be helpful if we scoped a very small proof\u2014one channel, one area, minimal governance\u2014just to validate clustering accuracy? Or is timing the bigger blocker?",
"link": "https://www.onyx.app/3310916-29"
},
{
"text": "David Green: Timing is big. And I\u2019m also getting pressure to show immediate ROI, not \u201cmaybe fewer spreadsheets.\u201d If it takes a quarter to prove out, I\u2019ll lose sponsorship.",
"link": "https://www.onyx.app/3310916-30"
},
{
"text": "Megan Foster: Understood. The fastest measurable outcome is reduction in duplicate triage effort and faster response for sales on known issues. But if you can\u2019t run even a 30-day focused pilot because of the migration, it may be better to revisit once your support workflows settle.",
"link": "https://www.onyx.app/3310916-31"
},
{
"text": "David Green: Yeah, I think that\u2019s where we are. I don\u2019t want to start something new, then have to rewire it in two months. The concept is good, but I can\u2019t commit to a buying process right now.",
"link": "https://www.onyx.app/3310916-32"
},
{
"text": "Megan Foster: That\u2019s fair. Why don\u2019t we do this: you tell me when your migration milestone hits\u2014maybe end of quarter\u2014and I\u2019ll send a short plan for a constrained pilot: SIS integrations only, Zendesk only, with success criteria around dedupe rate and time-to-answer for sales. If it\u2019s not compelling quickly, we stop.",
"link": "https://www.onyx.app/3310916-33"
},
{
"text": "David Green: That would be okay. Send me the pilot outline and what you\u2019d need from our side\u2014time, data access, who owns what.",
"link": "https://www.onyx.app/3310916-34"
},
{
"text": "Megan Foster: Will do. And David, before we wrap\u2014are there any internal objections you already anticipate so I can address them in the outline? Sounds like governance overhead and data completeness are the big ones.",
"link": "https://www.onyx.app/3310916-35"
},
{
"text": "David Green: Yes. And skepticism about any scoring becoming a political football. If you keep it focused on evidence and clustering, it\u2019ll land better.",
"link": "https://www.onyx.app/3310916-36"
},
{
"text": "Megan Foster: Got it. I\u2019ll keep the plan strictly on clustering and the evidence trail, with optional scoring later. I\u2019ll send that over this afternoon.",
"link": "https://www.onyx.app/3310916-37"
},
{
"text": "David Green: Thanks, Megan. Let\u2019s regroup later\u2014probably after we get through the migration.",
"link": "https://www.onyx.app/3310916-38"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "david_green@prairielark_education.onyx.app"
}
]
}

View File

@@ -0,0 +1,193 @@
{
"id": "FIREFLIES_7204170",
"semantic_identifier": "7204170 - Apexnova Solutions - sales_call - Discussion with Maria Lane",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-21 00:00:00",
"metadata": {
"meeting_date": "2025-05-21 00:00:00",
"duration_min": "34"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "7204170 - Apexnova Solutions - sales_call - Discussion with Maria Lane",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi Maria, good to see you again. Last time you mentioned you were interested in tightening up ICP and creating distinct messaging for SMB versus mid-market. I wanted to focus today on how Customer_Analyzer\u2019s positioning recommendations can help you get to segment-specific landing pages and outbound copy you can actually use.",
"link": "https://www.onyx.app/5747900-1"
},
{
"text": "Maria Lane: Hi Megan. Yes, we\u2019re still interested in the concept. We just need to understand how reliable the outputs are before we commit to anything.",
"link": "https://www.onyx.app/5747900-2"
},
{
"text": "Megan Foster: Totally fair. Let me anchor on one workflow: you connect the signals you already have\u2014CRM fields, onboarding survey answers, product usage events, support tickets, even interview notes\u2014and Customer_Analyzer produces positioning blocks for each segment. For example, \u201cSMB IT teams with limited admin bandwidth\u201d versus \u201cmid-market ops teams scaling processes.\u201d Then it generates an editable value prop, key differentiators, proof points to collect, and objection handling, all tailored to that segment.",
"link": "https://www.onyx.app/5747900-3"
},
{
"text": "Maria Lane: When you say \u201cgenerates,\u201d that\u2019s where my team gets nervous. We\u2019ve seen tools spit out generic messaging that sounds good but doesn\u2019t match reality. How do we know it\u2019s not a black box?",
"link": "https://www.onyx.app/5747900-4"
},
{
"text": "Megan Foster: Good question. The recommendations aren\u2019t just a paragraph of copy\u2014they\u2019re linked to drivers and evidence. So if it recommends a differentiator like \u201cfaster time-to-value with minimal setup,\u201d it will show what signals supported that\u2014like shorter onboarding time for a cohort, or repeated language from support tickets and interviews that points to setup friction being a key pain.",
"link": "https://www.onyx.app/5747900-5"
},
{
"text": "Maria Lane: Okay, but we don\u2019t have super clean data. Our CRM is messy, and usage events are partial. If the inputs are noisy, the outputs might be confident but wrong. That\u2019s my concern.",
"link": "https://www.onyx.app/5747900-6"
},
{
"text": "Megan Foster: That\u2019s a common starting point. We don\u2019t require perfection; we start with what you trust most. For this use case\u2014SMB vs mid-market messaging\u2014we can begin with a smaller set: firmographics from CRM plus onboarding survey responses, and maybe a couple of key usage milestones. Then as you validate, you can add more signals like support tags or call notes to sharpen it.",
"link": "https://www.onyx.app/5747900-7"
},
{
"text": "Maria Lane: \u201cValidate\u201d how? If we\u2019re going to rewrite landing pages and outbound sequences, I need my marketing lead to trust the rationale. Otherwise we\u2019re just debating the AI.",
"link": "https://www.onyx.app/5747900-8"
},
{
"text": "Megan Foster: The way teams usually validate is by using the editable messaging blocks as hypotheses. For example, for SMB, it might produce a hero headline emphasizing simplicity and speed, plus three bullets tied to top SMB use cases. For mid-market, it may emphasize scalability and governance. Then you run a quick A/B test on landing pages, or you pilot the new outbound openers with a small SDR pod for two weeks and compare reply rates. The tool also gives you \u201cproof points to collect,\u201d so you\u2019re not stuck with abstract claims.",
"link": "https://www.onyx.app/5747900-9"
},
{
"text": "Maria Lane: We\u2019re not in a place where we can run a lot of experiments. And honestly, rewriting our website is a big lift. Also, we already have positioning work from last year\u2014our VP will ask why we need a tool for this.",
"link": "https://www.onyx.app/5747900-10"
},
{
"text": "Megan Foster: Understood. The pitch isn\u2019t \u201cthrow out what you have.\u201d It\u2019s \u201cmake it segment-specific and consistent across teams.\u201d If last year\u2019s positioning is broad, Customer_Analyzer helps you translate it into two tiers\u2014SMB and mid-market\u2014so your website hero, sales deck bullets, and outbound intros align. It\u2019s meant to reduce internal churn and speed execution.",
"link": "https://www.onyx.app/5747900-11"
},
{
"text": "Maria Lane: I hear that, but I\u2019m still not convinced it won\u2019t produce something we could have written ourselves in a workshop. We\u2019re being asked to pay for\u2026 messaging blocks.",
"link": "https://www.onyx.app/5747900-12"
},
{
"text": "Megan Foster: Fair push. The differentiation is the linkage to observed customer language and the drivers behind why a segment is a better fit. It\u2019s not just copywriting\u2014it\u2019s connecting \u201cwho wins and why\u201d to what you say. That\u2019s where teams see improved win rates and shorter cycles, because reps aren\u2019t improvising and marketing isn\u2019t guessing.",
"link": "https://www.onyx.app/5747900-13"
},
{
"text": "Maria Lane: But do you actually show the \u201cobserved customer language\u201d? Because if I can\u2019t point to quotes or patterns, my stakeholders will say it\u2019s made up.",
"link": "https://www.onyx.app/5747900-14"
},
{
"text": "Megan Foster: We can surface themes and examples pulled from the inputs\u2014like recurring phrases from onboarding surveys or tagged support issues\u2014so you can say, \u201cmid-market consistently mentions compliance workflows,\u201d that kind of thing.",
"link": "https://www.onyx.app/5747900-15"
},
{
"text": "Maria Lane: \u201cThemes and examples\u201d sounds squishy. We have strict privacy rules too. We can\u2019t just pipe support tickets into a tool without legal reviewing it. That\u2019s another blocker.",
"link": "https://www.onyx.app/5747900-16"
},
{
"text": "Megan Foster: On privacy, we can scope it to the data sources you\u2019re comfortable with to start\u2014CRM plus survey fields\u2014without touching support tickets until you\u2019re ready. The goal today is to map a low-risk pilot that proves value.",
"link": "https://www.onyx.app/5747900-17"
},
{
"text": "Maria Lane: Even with just CRM and surveys, I\u2019m worried it\u2019ll still feel like: you upload data, it tells you what your ICP is, and we\u2019re supposed to trust it. We\u2019ve been burned by \u201cfit scores\u201d from other vendors.",
"link": "https://www.onyx.app/5747900-18"
},
{
"text": "Megan Foster: That\u2019s why I\u2019m focusing less on fit scores and more on positioning recommendations for the two ICP tiers you already use. We\u2019re not asking you to change your segmentation on day one. We\u2019re helping you refine messaging and objection handling per tier, based on the signals you have.",
"link": "https://www.onyx.app/5747900-19"
},
{
"text": "Maria Lane: Okay, but your product still generates it. If my team disagrees, what happens? We spend time arguing with the tool instead of making decisions.",
"link": "https://www.onyx.app/5747900-20"
},
{
"text": "Megan Foster: If there\u2019s disagreement, that\u2019s actually useful\u2014because you can trace back what\u2019s driving a recommendation and decide whether to adjust. The messaging blocks are editable, so you can keep what resonates and discard what doesn\u2019t.",
"link": "https://www.onyx.app/5747900-21"
},
{
"text": "Maria Lane: Editable is nice, but it also reinforces my point: if we\u2019re editing everything, are we really saving time? My team\u2019s bandwidth is limited.",
"link": "https://www.onyx.app/5747900-22"
},
{
"text": "Megan Foster: The time savings comes from not starting from blank pages, and from having a consistent structure\u2014value prop, differentiators, proof points, objections\u2014for both SMB and mid-market. Many teams can get to a first draft for two landing pages and two outbound email openers in a week instead of a month.",
"link": "https://www.onyx.app/5747900-23"
},
{
"text": "Maria Lane: We\u2019re not going to rebuild landing pages in a week. And outbound messaging changes require coordination with sales leadership. I\u2019m trying to be realistic.",
"link": "https://www.onyx.app/5747900-24"
},
{
"text": "Megan Foster: That\u2019s fair. Let\u2019s narrow it further then: we can start with outbound only. Two email openers per segment, plus an objection-handling snippet for your reps. Minimal lift, quick feedback.",
"link": "https://www.onyx.app/5747900-25"
},
{
"text": "Maria Lane: I appreciate the attempt to simplify, but I still don\u2019t have confidence the insights will be credible with our data quality. Also, my VP is going to ask for references in our industry and clear proof this isn\u2019t generic AI copy.",
"link": "https://www.onyx.app/5747900-26"
},
{
"text": "Megan Foster: We can provide references and a sample output. If you\u2019re open, we can do a light workshop: you send a small CSV export of CRM fields plus your onboarding survey responses, and we generate SMB vs mid-market positioning blocks for review.",
"link": "https://www.onyx.app/5747900-27"
},
{
"text": "Maria Lane: We\u2019re not comfortable sending exports without a full security review, and that process takes months here. And without seeing exactly how your \u201cevidence\u201d is derived, I can\u2019t champion it internally.",
"link": "https://www.onyx.app/5747900-28"
},
{
"text": "Megan Foster: Understood. If the security review timeline is months, we can explore doing a demo using anonymized or synthetic data that mirrors your structure\u2014just to validate the workflow and outputs.",
"link": "https://www.onyx.app/5747900-29"
},
{
"text": "Maria Lane: A demo with synthetic data won\u2019t persuade my stakeholders. They\u2019ll say it\u2019s staged. We\u2019d need to see it on our real signals, and we can\u2019t do that quickly.",
"link": "https://www.onyx.app/5747900-30"
},
{
"text": "Megan Foster: That\u2019s a real constraint. Would it help if we started with a no-data approach\u2014manually inputting a few segment notes and your current positioning, and then using the tool to generate structured messaging blocks from that?",
"link": "https://www.onyx.app/5747900-31"
},
{
"text": "Maria Lane: Honestly, no. Then it\u2019s definitely just AI copy. The only reason I\u2019d consider this is if it\u2019s grounded in our actual customer behavior and language, and we\u2019re not positioned to provide that data right now.",
"link": "https://www.onyx.app/5747900-32"
},
{
"text": "Megan Foster: Got it. It sounds like the blockers are: trust in the rationale, data quality, and security review timelines. If we can\u2019t get to real data access, it\u2019s hard to prove value.",
"link": "https://www.onyx.app/5747900-33"
},
{
"text": "Maria Lane: Exactly. I\u2019m not saying \u201cnever,\u201d but \u201cnot now.\u201d We have too many initiatives this quarter, and the black-box perception will be an uphill battle internally.",
"link": "https://www.onyx.app/5747900-34"
},
{
"text": "Megan Foster: I appreciate the clarity. Would it make sense to pause and revisit next quarter after you\u2019ve had time to align internally on data access and what evidence your team would need to trust recommendations?",
"link": "https://www.onyx.app/5747900-35"
},
{
"text": "Maria Lane: Yes, let\u2019s do that. If you can send over a one-pager on what specific fields you\u2019d need for the SMB vs mid-market use case, and what the security review typically entails, I can at least socialize it.",
"link": "https://www.onyx.app/5747900-36"
},
{
"text": "Megan Foster: Absolutely. I\u2019ll send a concise list of minimum viable inputs, plus how we tie recommendations to evidence, and an overview of the security items your team will ask about. Thanks Maria\u2014this was helpful, even if the timing isn\u2019t right.",
"link": "https://www.onyx.app/5747900-37"
},
{
"text": "Maria Lane: Thanks Megan. Send that over and we\u2019ll circle back when we\u2019re in a better place.",
"link": "https://www.onyx.app/5747900-38"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "maria_lane@apexnova_solutions.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_7643698",
"semantic_identifier": "7643698 - Pebble & Pine Coffee - sales_pitch - Discussion with Allison Peterson",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-30 00:00:00",
"metadata": {
"meeting_date": "2025-05-30 00:00:00",
"duration_min": "25"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "7643698 - Pebble & Pine Coffee - sales_pitch - Discussion with Allison Peterson",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi Allison\u2014Megan Foster here. Thanks for taking a few minutes. I work with revenue teams that live in Salesforce or HubSpot but need a clearer, more consistent way to turn activity and pipeline signals into \u201cwhat do I do next\u201d and a forecast they can actually trust. Is now still good for a quick intro?",
"link": "https://www.onyx.app/6588656-1"
},
{
"text": "Allison Peterson: Yep, I\u2019ve got about five minutes. What are you calling about?",
"link": "https://www.onyx.app/6588656-2"
},
{
"text": "Megan Foster: Perfect. I wanted to share Sales_Accelerator\u2014it's a sales execution layer that sits on top of your CRM. It doesn\u2019t replace what you already use; it pulls in your existing opportunity stages plus real activity signals\u2014emails, calls, meetings\u2014then gives you explainable forecasting and best-next-action guidance so deals don\u2019t stall quietly. Quick question: at Pebble & Pine Coffee, where do you feel the most pain\u2014forecast accuracy at the end of the month/quarter, or deals slipping after discovery?",
"link": "https://www.onyx.app/6588656-3"
},
{
"text": "Allison Peterson: Honestly, both. Forecasts get squishy, and we definitely have deals that go dark after discovery.",
"link": "https://www.onyx.app/6588656-4"
},
{
"text": "Megan Foster: That\u2019s exactly the combo we built for. On the forecast side, Sales_Accelerator produces commit/best-case/pipeline views, but it doesn\u2019t just echo CRM stage. It uses drivers like stage age, last touch, whether a next meeting is actually scheduled, and historical cycle-time benchmarks\u2014so you can see *why* something is likely to close or slip.",
"link": "https://www.onyx.app/6588656-5"
},
{
"text": "Allison Peterson: We have some dashboards already. How is this different from reporting in Salesforce?",
"link": "https://www.onyx.app/6588656-6"
},
{
"text": "Megan Foster: Great question. Traditional dashboards show \u201cwhat is,\u201d but they don\u2019t reliably connect activity to outcome, and they rarely explain slippage early enough to fix it. Sales_Accelerator flags risk patterns\u2014like \u201cthis deal is in stage 2 twice as long as your benchmark and hasn\u2019t had a live touch in 10 days\u201d\u2014then recommends the next step to get it moving. It\u2019s proactive, not just retrospective.",
"link": "https://www.onyx.app/6588656-7"
},
{
"text": "Allison Peterson: Okay. But we\u2019re a lean team\u2014if it\u2019s another tool that requires a lot of admin work, it\u2019ll be tough.",
"link": "https://www.onyx.app/6588656-8"
},
{
"text": "Megan Foster: Totally fair. The setup is light because it sits on top of your existing CRM and activity data. The biggest change is actually in day-to-day behavior: reps get a clear queue of what to do next, and managers get an early-warning layer before end-of-quarter surprises. You\u2019re not building a new process; you\u2019re making the current one more consistent and visible.",
"link": "https://www.onyx.app/6588656-9"
},
{
"text": "Allison Peterson: What does \u201cwhat to do next\u201d look like? Like tasks?",
"link": "https://www.onyx.app/6588656-10"
},
{
"text": "Megan Foster: Exactly\u2014actionable recommendations tied to the deal. One common use case is right after discovery. The tool will help the AE follow up fast and consistently: it drafts a recap email, suggests a couple tailored proof points from your approved positioning library, and generates a mutual action plan with due dates and stakeholders so you can multi-thread instead of relying on a single champion.",
"link": "https://www.onyx.app/6588656-11"
},
{
"text": "Allison Peterson: The recap email piece is interesting. Our reps are inconsistent there, and we lose momentum.",
"link": "https://www.onyx.app/6588656-12"
},
{
"text": "Megan Foster: That\u2019s a very common failure point. When the follow-up is late or vague, the buyer\u2019s urgency evaporates. Sales_Accelerator nudges the rep immediately post-call\u2014\u201csend recap within 2 hours,\u201d \u201cconfirm next meeting,\u201d \u201cadd stakeholders,\u201d \u201cpropose MAP dates\u201d\u2014and it tracks whether that actually happened. If the next meeting isn\u2019t scheduled, it marks it as an elevated risk and surfaces it to the manager as well.",
"link": "https://www.onyx.app/6588656-13"
},
{
"text": "Allison Peterson: How does it know what proof points to use? We don\u2019t have a huge enablement team.",
"link": "https://www.onyx.app/6588656-14"
},
{
"text": "Megan Foster: You don\u2019t need a huge team. You\u2019d load a small set of approved positioning blocks\u2014think a lightweight library of \u201chere are our best proof points by persona or problem.\u201d Sales_Accelerator recommends two that fit the context, so reps aren\u2019t improvising. If you already have something like that in docs, we can start small and refine.",
"link": "https://www.onyx.app/6588656-15"
},
{
"text": "Allison Peterson: And the forecast\u2014how quickly would we see that improve? Everyone says they have \u201cbetter forecasting.\u201d",
"link": "https://www.onyx.app/6588656-16"
},
{
"text": "Megan Foster: Fair skepticism. The improvement comes from two things: first, we\u2019re combining stage data with actual activity signals and cycle-time benchmarks; second, the output is explainable\u2014so your team trusts it and can act on it. Instead of a black-box number, it will tell you the drivers: \u201cstage age is high,\u201d \u201cno meeting scheduled,\u201d \u201cno recent touch,\u201d \u201chistorically similar deals convert at X%.\u201d That helps managers coach *before* a slip becomes permanent.",
"link": "https://www.onyx.app/6588656-17"
},
{
"text": "Allison Peterson: We definitely struggle to diagnose why things slip until it\u2019s too late.",
"link": "https://www.onyx.app/6588656-18"
},
{
"text": "Megan Foster: Exactly. And it also helps with scenario planning. For example, you can ask, \u201cIf we add 20 SQLs in our best segment, what impact should we expect?\u201d That\u2019s useful for planning promos, partner pushes, or just aligning leadership on what pipeline is needed to hit target.",
"link": "https://www.onyx.app/6588656-19"
},
{
"text": "Allison Peterson: That would help. We\u2019ve been guessing a lot when we plan our next quarter.",
"link": "https://www.onyx.app/6588656-20"
},
{
"text": "Megan Foster: Makes sense. One more quick question so I don\u2019t assume: are your AEs the ones running discovery and follow-up, or do you have SDRs handing off?",
"link": "https://www.onyx.app/6588656-21"
},
{
"text": "Allison Peterson: We have SDRs setting meetings, but AEs own discovery and everything after.",
"link": "https://www.onyx.app/6588656-22"
},
{
"text": "Megan Foster: Perfect fit for the post-discovery use case then. We see the fastest ROI when AEs tighten that follow-up motion\u2014recap email, proof points, mutual plan, stakeholder mapping\u2014and managers get a clear view of which deals are drifting and why. That combination tends to lift conversion and reduce forecast surprises.",
"link": "https://www.onyx.app/6588656-23"
},
{
"text": "Allison Peterson: I like the sound of it. What would the next step be?",
"link": "https://www.onyx.app/6588656-24"
},
{
"text": "Megan Foster: I\u2019d suggest a 30-minute working session where we connect the dots to your process: what CRM you\u2019re on, what \u201cstage age\u201d looks like for you, what activity signals you capture, and we can walk through a sample opportunity flow\u2014especially the discovery-to-next-step moment. If you\u2019re open to it, we can also show what the forecast view looks like by rep/segment and how risk alerts surface.",
"link": "https://www.onyx.app/6588656-25"
},
{
"text": "Allison Peterson: Yeah, I\u2019m open to that. Can you loop in my sales ops lead too?",
"link": "https://www.onyx.app/6588656-26"
},
{
"text": "Megan Foster: Absolutely. Who should I include, and what CRM are you primarily using today\u2014Salesforce or HubSpot?",
"link": "https://www.onyx.app/6588656-27"
},
{
"text": "Allison Peterson: Salesforce. And I\u2019ll add Jordan Kim from sales ops.",
"link": "https://www.onyx.app/6588656-28"
},
{
"text": "Megan Foster: Great\u2014Salesforce is very common for us. I\u2019ll send a calendar invite for 30 minutes with you and Jordan. Do mornings or afternoons usually work better next week?",
"link": "https://www.onyx.app/6588656-29"
},
{
"text": "Allison Peterson: Tuesday afternoon or Thursday morning would work.",
"link": "https://www.onyx.app/6588656-30"
},
{
"text": "Megan Foster: Perfect. I\u2019ll propose Tuesday at 2:00 and Thursday at 10:00, and you can pick what\u2019s best. Before that meeting, if you can share your current stage definitions and a rough sense of your average sales cycle, we\u2019ll tailor the demo so it\u2019s not generic.",
"link": "https://www.onyx.app/6588656-31"
},
{
"text": "Allison Peterson: Sure\u2014send me what you need and I\u2019ll see what I can pull together.",
"link": "https://www.onyx.app/6588656-32"
},
{
"text": "Megan Foster: Will do. Thanks, Allison\u2014appreciate the time today. I\u2019ll get that invite over in the next few minutes.",
"link": "https://www.onyx.app/6588656-33"
},
{
"text": "Allison Peterson: Sounds good. Talk soon.",
"link": "https://www.onyx.app/6588656-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "allison_peterson@pebble_&_pine_coffee.onyx.app"
}
]
}

View File

@@ -0,0 +1,185 @@
{
"id": "FIREFLIES_7792985",
"semantic_identifier": "7792985 - Keystone Atlas Partners - sales_call - Discussion with Mason Davis",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-15 00:00:00",
"metadata": {
"meeting_date": "2025-05-15 00:00:00",
"duration_min": "45"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "7792985 - Keystone Atlas Partners - sales_call - Discussion with Mason Davis",
"organizer_email": "irene_shen@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Irene Shen: Hi Mason\u2014good to see you again. Last time you mentioned the team liked the idea of tightening follow-through after discovery and getting fewer \u201csurprise\u201d slips late in the quarter. Does that still feel like the priority, or has anything shifted?",
"link": "https://www.onyx.app/1167175-1"
},
{
"text": "Mason Davis: Hi Irene. No, that\u2019s still the priority. We\u2019re generating decent pipeline, but deals stall in weird ways\u2014someone forgets to send a recap, next steps aren\u2019t clear, or we\u2019re single-threaded and don\u2019t realize it until it\u2019s too late.",
"link": "https://www.onyx.app/1167175-2"
},
{
"text": "Irene Shen: That\u2019s exactly where Sales_Accelerator tends to land well. For today, I want to focus on one core thing: our sales follow-up recommendations\u2014how we guide the AE on what to do next, right inside your CRM, based on what actually happened in the deal. Then we can talk through what it\u2019d look like at Keystone Atlas Partners to get to a \u201cyes.\u201d",
"link": "https://www.onyx.app/1167175-3"
},
{
"text": "Mason Davis: Sounds good. I remember you said it sits on top of Salesforce, right?",
"link": "https://www.onyx.app/1167175-4"
},
{
"text": "Irene Shen: Correct\u2014Sales_Accelerator sits on top of Salesforce or HubSpot. We\u2019re not replacing your CRM, and we\u2019re not redoing ICP/positioning work you\u2019ve already invested in. We integrate with Customer_Analyzer\u2014so we can consume fit scores and approved positioning blocks\u2014and then translate that into next-best-action guidance for reps on each opportunity.",
"link": "https://www.onyx.app/1167175-5"
},
{
"text": "Mason Davis: Okay. Walk me through the follow-up recommendations piece in a concrete way.",
"link": "https://www.onyx.app/1167175-6"
},
{
"text": "Irene Shen: Sure. Let\u2019s take your typical flow: an AE runs a discovery call, the meeting happens, and then\u2026 sometimes a recap email goes out, sometimes it doesn\u2019t, sometimes it\u2019s generic, and the deal gets fuzzy. Sales_Accelerator detects \u201cmeeting occurred without follow-up\u201d and recommends a next action: \u201cSend recap + confirm next steps.\u201d It can draft that recap email, and it will pull in two tailored proof points from your approved positioning library\u2014coming from Customer_Analyzer\u2014so the rep isn\u2019t inventing messaging.",
"link": "https://www.onyx.app/1167175-7"
},
{
"text": "Mason Davis: When you say \u201cdraft,\u201d is it like an email template, or is it generated each time?",
"link": "https://www.onyx.app/1167175-8"
},
{
"text": "Irene Shen: Generated each time, grounded in the opportunity context\u2014stage, stakeholders engaged, what meetings occurred, and recent activity. Reps can edit before sending, of course. The goal is to reduce the variance: every deal gets a timely recap with clear next steps and the right proof points for that account\u2019s positioning.",
"link": "https://www.onyx.app/1167175-9"
},
{
"text": "Mason Davis: That\u2019s helpful. Our best reps do that consistently, but newer reps don\u2019t. And managers end up chasing them.",
"link": "https://www.onyx.app/1167175-10"
},
{
"text": "Irene Shen: Exactly. The second part is the mutual action plan checklist. If the deal is missing basics\u2014like no agreed timeline, no economic buyer identified, or no next meeting scheduled\u2014Sales_Accelerator suggests creating a lightweight mutual action plan with due dates and stakeholders. It\u2019s not a heavy methodology; it\u2019s a checklist the rep can send to the customer and track. And it will nudge multi-threading when only one contact is active.",
"link": "https://www.onyx.app/1167175-11"
},
{
"text": "Mason Davis: Multi-threading is a big one for us. We think we have a deal and then legal or finance shows up at the end and everything pauses.",
"link": "https://www.onyx.app/1167175-12"
},
{
"text": "Irene Shen: Right\u2014so if your opportunity has, say, one active champion but no other stakeholders engaged, we recommend the next action: \u201cMulti-thread\u2014add procurement/finance/IT stakeholder based on deal type.\u201d The rep gets a suggested outreach sequence: a short cadence with channel mix\u2014email + LinkedIn, and a call task\u2014so it\u2019s not just a reminder; it\u2019s an executable plan in a task queue inside Salesforce or via a browser extension.",
"link": "https://www.onyx.app/1167175-13"
},
{
"text": "Mason Davis: Got it. Let me push on a concern: our Salesforce data hygiene is\u2026 mixed. Stages aren\u2019t always updated, close dates drift, and next steps fields aren\u2019t consistent. If your recommendations depend on that, won\u2019t we get a bunch of false alerts?",
"link": "https://www.onyx.app/1167175-14"
},
{
"text": "Irene Shen: That\u2019s a fair concern and honestly the most common risk area. Two things we do to manage it. First, the recommendations don\u2019t rely on one field; they triangulate across activity signals\u2014meetings logged, emails sent, stage age, stakeholder count, and whether a mutual plan exists. So even if stage is stale, we can still catch \u201cmeeting happened but no follow-up,\u201d which is usually activity-driven.",
"link": "https://www.onyx.app/1167175-15"
},
{
"text": "Mason Davis: Okay, but forecasts and \u201cdeal at risk\u201d alerts can still get noisy if close dates are sloppy.",
"link": "https://www.onyx.app/1167175-16"
},
{
"text": "Irene Shen: Agreed. We typically start with follow-up recommendations first\u2014because it\u2019s easiest to prove value without needing perfect data. In parallel, we identify 3\u20135 hygiene rules that matter most: consistent next-step logging, stage exit criteria, and close date update discipline. We can even configure alert thresholds so you don\u2019t get alert fatigue\u2014fewer, higher-confidence recommendations at first, then expand once reps trust it.",
"link": "https://www.onyx.app/1167175-17"
},
{
"text": "Mason Davis: That makes sense. I like the idea of starting where the data dependency is lower.",
"link": "https://www.onyx.app/1167175-18"
},
{
"text": "Irene Shen: Great. Let me sanity-check the use case with your process. After discovery, what are the most common misses? Is it recap, is it scheduling the next meeting, is it stakeholder mapping?",
"link": "https://www.onyx.app/1167175-19"
},
{
"text": "Mason Davis: Recap and next meeting are the top two. Stakeholder mapping is third, especially on larger deals.",
"link": "https://www.onyx.app/1167175-20"
},
{
"text": "Irene Shen: Perfect\u2014then we\u2019d configure your top recommended actions around those moments. Example: \u201cDiscovery held \u2192 no follow-up in 24 hours\u201d triggers a drafted recap with two positioning proof points. \u201cNo next meeting scheduled \u2192 propose 2 time options + agenda.\u201d And \u201conly one contact active \u2192 multi-thread suggestion + message tailored to the stakeholder role.\u201d",
"link": "https://www.onyx.app/1167175-21"
},
{
"text": "Mason Davis: How much work is it for us to get the positioning proof points into Customer_Analyzer? We have messaging, but it\u2019s scattered.",
"link": "https://www.onyx.app/1167175-22"
},
{
"text": "Irene Shen: Since this is your second call, I\u2019ll keep it practical: we don\u2019t need everything on day one. We can start with a small approved library\u2014like top 10 proof points and 5 common objections\u2014and map them to a few customer segments. Sales_Accelerator then uses that to populate the follow-up drafts. Over time you can expand it, but you\u2019ll see value quickly with a minimal set.",
"link": "https://www.onyx.app/1167175-23"
},
{
"text": "Mason Davis: That\u2019s good. Another concern: reps hate extra tools. If this feels like yet another system, adoption will be tough.",
"link": "https://www.onyx.app/1167175-24"
},
{
"text": "Irene Shen: Totally. That\u2019s why we deliver the task queue and recommendations inside Salesforce wherever possible. The rep sees: \u201cHere are your next 5 actions today,\u201d clicks into the opportunity, sends the drafted recap, and checks off the mutual plan item. We can also run it via extension, but the north star is: no tab-switching, no duplicate data entry.",
"link": "https://www.onyx.app/1167175-25"
},
{
"text": "Mason Davis: Okay. If we did this, what would a pilot look like?",
"link": "https://www.onyx.app/1167175-26"
},
{
"text": "Irene Shen: I\u2019d recommend a 30-day pilot with a small AE group\u2014maybe 6\u20138 reps\u2014focused only on follow-up recommendations for post-discovery and early-stage progression. Success metrics would be simple: follow-up sent within 24 hours after discovery, next meeting scheduled rate, multi-threading rate, and reduction in \u201cstalled\u201d opportunities where there\u2019s no activity for X days.",
"link": "https://www.onyx.app/1167175-27"
},
{
"text": "Mason Davis: And what do you need from us?",
"link": "https://www.onyx.app/1167175-28"
},
{
"text": "Irene Shen: Three things: (1) Salesforce access for integration, (2) a lightweight agreement on the 3\u20135 data hygiene rules we enforce during the pilot\u2014nothing crazy, just consistency, and (3) a starter positioning set in Customer_Analyzer\u2014those proof points and objections we mentioned. I can send a template for that so it\u2019s not a heavy lift.",
"link": "https://www.onyx.app/1167175-29"
},
{
"text": "Mason Davis: This feels like it maps pretty closely to what we\u2019re trying to fix. What\u2019s the typical time to get it live?",
"link": "https://www.onyx.app/1167175-30"
},
{
"text": "Irene Shen: Integration and initial configuration is usually 1\u20132 weeks, then you\u2019re running the pilot. If we start next week, you could have reps getting recommendations inside Salesforce by mid-month.",
"link": "https://www.onyx.app/1167175-31"
},
{
"text": "Mason Davis: Alright. I\u2019m interested. What are the next steps to move toward a purchase?",
"link": "https://www.onyx.app/1167175-32"
},
{
"text": "Irene Shen: Great\u2014here\u2019s what I propose. I\u2019ll send a short pilot plan today: scope, metrics, and the minimal positioning library request. Then we schedule a 30-minute working session with you to confirm the workflow triggers\u2014post-discovery recap, next meeting scheduling, and multi-threading. If you\u2019re comfortable after that, we can align on pricing for the pilot converting to an annual agreement, and get procurement started.",
"link": "https://www.onyx.app/1167175-33"
},
{
"text": "Mason Davis: That works. Send the pilot plan, and let\u2019s book that working session. If the pricing is reasonable and the Salesforce lift isn\u2019t too heavy, I think we can get this approved.",
"link": "https://www.onyx.app/1167175-34"
},
{
"text": "Irene Shen: Perfect. I\u2019ll email the plan within the hour and include two time options for early next week. Thanks, Mason\u2014this is a good fit for what you described, and I\u2019m confident we can show value quickly without drowning your team in alerts.",
"link": "https://www.onyx.app/1167175-35"
},
{
"text": "Mason Davis: Thanks, Irene. Looking forward to it.",
"link": "https://www.onyx.app/1167175-36"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "irene_shen@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "mason_davis@keystone_atlas_partners.onyx.app"
}
]
}

View File

@@ -0,0 +1,137 @@
{
"id": "FIREFLIES_8306506",
"semantic_identifier": "8306506 - Fairway Prism Sports - sales_pitch - Discussion with Natalie Wheeler",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-22 00:00:00",
"metadata": {
"meeting_date": "2025-05-22 00:00:00",
"duration_min": "34"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "8306506 - Fairway Prism Sports - sales_pitch - Discussion with Natalie Wheeler",
"organizer_email": "camila_vega@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Camila Vega: Hi Natalie\u2014Camila Vega here. Thanks for taking a few minutes. I\u2019ll keep this quick. I\u2019m calling because we built Customer_Analyzer to help teams get really crisp on who their best customers are and then translate that into messaging your reps can actually use in deals. Does that map at all to what you\u2019re working on at Fairway Prism Sports?",
"link": "https://www.onyx.app/3678912-1"
},
{
"text": "Natalie Wheeler: Hi Camila. I can spare a few minutes, but just to set expectations\u2014we\u2019re in the middle of planning and I\u2019m not really looking at new tools right now.",
"link": "https://www.onyx.app/3678912-2"
},
{
"text": "Camila Vega: Totally fair. The 60-second version is: Customer_Analyzer ingests signals you already have\u2014CRM fields, onboarding survey answers, product usage events, support tickets, even interview notes\u2014and produces two outputs. One is a fit score from 0 to 100 at the account and segment level, and the other is positioning recommendations: value props, differentiators, proof points, and objection handling tied to what actually predicts retention and expansion. \nWhere I see it helping teams quickly is when there\u2019s disagreement internally on ICP, or when sales is chasing deals that look good on paper but don\u2019t renew. Is that a pain you\u2019re seeing?",
"link": "https://www.onyx.app/3678912-3"
},
{
"text": "Natalie Wheeler: We\u2019ve done a lot of ICP work already. We have segments, we have personas. It\u2019s not perfect, but it\u2019s not the main blocker.",
"link": "https://www.onyx.app/3678912-4"
},
{
"text": "Camila Vega: That\u2019s great you already have it documented\u2014honestly that\u2019s a better starting point than most. What we typically find is the \u201cpaper ICP\u201d exists, but it\u2019s not anchored to outcomes. \nWith the fit estimation feature, you can define fit drivers\u2014industry, size, problem severity, willingness to pay, time-to-value, adoption patterns, support burden, churn risk signals\u2014and we break down *why* a segment scores higher or lower, then compare segments like SMB vs mid-market. The practical result is you can say, \u201cThis is our highest-likelihood segment for renewal/expansion,\u201d and align marketing and sales around it. \nOut of curiosity, how do you currently decide whether a deal is a great fit before you invest a lot of sales cycles?",
"link": "https://www.onyx.app/3678912-5"
},
{
"text": "Natalie Wheeler: Mostly rep judgment plus a few qualification questions. We look at basics in the CRM. But again, we\u2019re not shopping for analytics platforms right now.",
"link": "https://www.onyx.app/3678912-6"
},
{
"text": "Camila Vega: Understood. Let me connect it to something more immediate: sales enablement. One common use case is building an objection-handling guide tied to the strongest value drivers for your best-fit accounts. \nSo instead of a generic battlecard, you get: \u201cFor high-fit Segment A, the top drivers are X and Y, here are proof points that resonate, and here\u2019s how to handle the top objections we see in that segment.\u201d That tends to shorten ramp time and keep reps from pitching the wrong story. \nDo you have a consistent objection pattern that keeps coming up\u2014pricing, switching costs, internal buy-in?",
"link": "https://www.onyx.app/3678912-7"
},
{
"text": "Natalie Wheeler: Pricing and \u201cwhy now\u201d are the big ones. But we already have enablement materials. They\u2019re not perfect, but we iterate.",
"link": "https://www.onyx.app/3678912-8"
},
{
"text": "Camila Vega: Makes sense. The difference is we tie the content to observed signals\u2014like \u201caccounts that adopt Feature Z within 30 days renew at a higher rate,\u201d or \u201cteams with high support burden churn more,\u201d and then we make the messaging reflect that reality. \nIf we took, say, your last 12 months of closed-won and churned accounts, we could produce a driver breakdown: what correlates with healthy customers vs risky ones, and then turn that into objection handling that\u2019s grounded in outcomes instead of anecdotes. \nIs Natalie the right person to evaluate something like that, or is that more RevOps/Sales Enablement/Marketing?",
"link": "https://www.onyx.app/3678912-9"
},
{
"text": "Natalie Wheeler: It\u2019s a mix, but I\u2019m not leading tooling decisions. Also, pulling in support tickets and product usage data is a bigger project than it sounds. We\u2019re already stretched.",
"link": "https://www.onyx.app/3678912-10"
},
{
"text": "Camila Vega: That\u2019s a fair callout. Implementation effort is usually the first concern. For a light start, many teams begin with just CRM + a few onboarding fields, then add richer signals later. Even with that, we can often produce meaningful segment comparisons and a fit score baseline. \nBut if bandwidth is the constraint, I don\u2019t want to push something that becomes \u201cone more system.\u201d \nCan I ask\u2014are you actively seeing churn issues or wasted pipeline from poor-fit deals, or is it mostly \u201cnice to have\u201d right now?",
"link": "https://www.onyx.app/3678912-11"
},
{
"text": "Natalie Wheeler: It\u2019s more \u201cnice to have.\u201d Our churn is acceptable, and pipeline quality isn\u2019t the biggest fire at the moment.",
"link": "https://www.onyx.app/3678912-12"
},
{
"text": "Camila Vega: Got it. In that case, it may simply be the wrong timing. The only reason I\u2019m reaching out is because teams similar to yours have used the fit scoring to reduce time spent on low-likelihood segments and to sharpen their pitch where it matters. But if you\u2019re not feeling that pain, a full evaluation probably isn\u2019t a good use of your week.",
"link": "https://www.onyx.app/3678912-13"
},
{
"text": "Natalie Wheeler: Yeah, I appreciate that. We have too many priorities right now and I don\u2019t want to take on an initiative that requires coordination across systems.",
"link": "https://www.onyx.app/3678912-14"
},
{
"text": "Camila Vega: Completely understood. Would it be helpful if I sent a one-page overview that explains the fit score output and what an objection-handling guide looks like when it\u2019s tied to fit drivers? That way, if priorities shift later, you have it on hand.",
"link": "https://www.onyx.app/3678912-15"
},
{
"text": "Natalie Wheeler: You can send it, sure, but I can\u2019t promise when I\u2019ll look at it.",
"link": "https://www.onyx.app/3678912-16"
},
{
"text": "Camila Vega: That\u2019s totally fine. What\u2019s the best email for you, and is there anyone else you\u2019d want looped in\u2014maybe whoever owns enablement or RevOps\u2014just so it doesn\u2019t get lost?",
"link": "https://www.onyx.app/3678912-17"
},
{
"text": "Natalie Wheeler: Send it to my work email. And no, don\u2019t loop anyone else right now.",
"link": "https://www.onyx.app/3678912-18"
},
{
"text": "Camila Vega: Will do. Last quick question so I tailor it: in your world, is the bigger challenge getting reps to qualify out faster, or getting them to tell a more consistent story to the right accounts?",
"link": "https://www.onyx.app/3678912-19"
},
{
"text": "Natalie Wheeler: Consistent story, but again\u2014we\u2019re managing it with our current process.",
"link": "https://www.onyx.app/3678912-20"
},
{
"text": "Camila Vega: Helpful. I\u2019ll send a short summary focused on consistent messaging tied to best-fit accounts, plus an example of how the driver breakdown maps to objections like pricing and \u201cwhy now.\u201d \nAnd Natalie, if in a quarter you find you\u2019re revisiting ICP or you see pipeline quality slipping, I\u2019m happy to reconnect\u2014no pressure. Sound good?",
"link": "https://www.onyx.app/3678912-21"
},
{
"text": "Natalie Wheeler: Sure. Thanks, Camila.",
"link": "https://www.onyx.app/3678912-22"
},
{
"text": "Camila Vega: Thanks for the time, Natalie. I\u2019ll get that over today. Have a good one.",
"link": "https://www.onyx.app/3678912-23"
},
{
"text": "Natalie Wheeler: You too. Bye.",
"link": "https://www.onyx.app/3678912-24"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "camila_vega@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "natalie_wheeler@fairway_prism_sports.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_8902959",
"semantic_identifier": "8902959 - Nimbunest Snacks - sales_pitch - Discussion with Jade Jacobs",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-05-08 00:00:00",
"metadata": {
"meeting_date": "2025-05-08 00:00:00",
"duration_min": "16"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-05"
],
"year_month": "2025-05",
"meeting_title": "8902959 - Nimbunest Snacks - sales_pitch - Discussion with Jade Jacobs",
"organizer_email": "arjun_mehta@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Arjun Mehta: Hi Jade\u2014Arjun Mehta here. Thanks for taking a few minutes today. I\u2019ll keep it brief. I wanted to share an idea that\u2019s helped teams reduce customer-issue chaos across Support, Engineering, and Sales\u2014especially when a deal is on the line.",
"link": "https://www.onyx.app/4475787-1"
},
{
"text": "Jade Jacobs: Hi Arjun. Okay\u2014quick is good. What\u2019s this about?",
"link": "https://www.onyx.app/4475787-2"
},
{
"text": "Arjun Mehta: Totally. The product is called Issue_Tracker. It\u2019s a layer that sits between your support channels\u2014like Zendesk or Intercom\u2014your delivery tools like Jira or Linear, and your customer context in Salesforce or HubSpot. It continuously ingests customer-reported issues, deduplicates them into clusters, and ties them back to impacted customers and opportunities\u2014so Sales can see what\u2019s real, what\u2019s duplicative noise, and what\u2019s actually threatening revenue.",
"link": "https://www.onyx.app/4475787-3"
},
{
"text": "Jade Jacobs: We already have Zendesk and Jira connected with some workflows. And our CSMs try to keep Salesforce updated. So what\u2019s different here?",
"link": "https://www.onyx.app/4475787-4"
},
{
"text": "Arjun Mehta: Great question. Most setups connect systems, but they don\u2019t standardize the \u201cissue object.\u201d Issue_Tracker creates a single, searchable issue cluster\u2014root cause + evidence + impacted accounts\u2014linked to every source ticket and to the downstream Jira work. The big difference is it stays defensible: you can click from the cluster to the original tickets, and from the cluster to the engineering item, without duplicate data entry or someone manually curating spreadsheets.",
"link": "https://www.onyx.app/4475787-5"
},
{
"text": "Jade Jacobs: We definitely have spreadsheets. But we also have a support ops person who does some tagging and triage. It\u2019s not perfect, but it works.",
"link": "https://www.onyx.app/4475787-6"
},
{
"text": "Arjun Mehta: That makes sense. Where Issue_Tracker tends to help is when \u201cit works\u201d until it doesn\u2019t\u2014like when Sales hears about an issue from a prospect, but Support has 37 tickets that are all slightly different phrasing, and Engineering has three Jira tickets that may or may not be the same root cause. With Issue_Tracker, you can set dedupe rules and field mapping so those become one theme, with consistent metadata, and a single status/ETA narrative that\u2019s safe for customer-facing teams.",
"link": "https://www.onyx.app/4475787-7"
},
{
"text": "Jade Jacobs: We\u2019re not trying to boil the ocean though. And we\u2019re pretty careful about what Sales says\u2014sometimes they overpromise.",
"link": "https://www.onyx.app/4475787-8"
},
{
"text": "Arjun Mehta: Exactly\u2014and that\u2019s one of the use cases I wanted to highlight. When a late-stage opportunity is tied to a known issue cluster, Issue_Tracker can surface the current status, any ETA notes, and even approved language\u2014what\u2019s safe to communicate\u2014so Sales doesn\u2019t freelanced their own story. It also integrates with Sales_Accelerator so the deal can be flagged \u201cat risk\u201d based on the issue\u2019s status and impact.",
"link": "https://www.onyx.app/4475787-9"
},
{
"text": "Jade Jacobs: We don\u2019t use Sales_Accelerator. And candidly, our Sales team doesn\u2019t always log things cleanly in Salesforce either.",
"link": "https://www.onyx.app/4475787-10"
},
{
"text": "Arjun Mehta: Understood. Even without Sales_Accelerator, the core value still stands: the connector to Salesforce can link clusters to accounts and opportunities, so you can prioritize issues based on business impact\u2014revenue exposure, ICP segment, or deal risk\u2014rather than whoever yelled loudest in Slack. If data hygiene is uneven, we usually start with light-touch field mapping: account ID, segment, opportunity stage\u2014just enough to be useful.",
"link": "https://www.onyx.app/4475787-11"
},
{
"text": "Jade Jacobs: How heavy is the setup? We don\u2019t have bandwidth for a big implementation.",
"link": "https://www.onyx.app/4475787-12"
},
{
"text": "Arjun Mehta: Setup is typically connectors plus mapping. For sources: Zendesk/Intercom/Help Scout. For delivery: Jira/Linear/GitHub. And for context: Salesforce/HubSpot plus Customer_Analyzer fit/segment IDs if you have them. You can start with just Zendesk + Jira + Salesforce. Then define dedupe rules\u2014like matching by error signature, keywords, product area, or custom fields. The goal is to keep Zendesk and Jira as systems of record; Issue_Tracker just synchronizes metadata and adds back-links so teams can navigate without re-entering anything.",
"link": "https://www.onyx.app/4475787-13"
},
{
"text": "Jade Jacobs: That\u2019s the concern\u2014tools on top of tools. We already have a lot of dashboards no one checks.",
"link": "https://www.onyx.app/4475787-14"
},
{
"text": "Arjun Mehta: Totally fair. The reason teams use this is it replaces the manual \u201ctranslation layer\u201d between teams\u2014spreadsheets, internal docs, recurring meetings to align on what\u2019s actually happening. Instead of another dashboard, it\u2019s more like: \u201cHere\u2019s the cluster, here\u2019s the evidence, here\u2019s impacted accounts, here\u2019s the Jira link, here\u2019s what we can safely tell customers.\u201d It becomes the one place to answer \u201cis this a known issue\u201d and \u201cwhat\u2019s the real status.\u201d",
"link": "https://www.onyx.app/4475787-15"
},
{
"text": "Jade Jacobs: We can usually answer \u201cknown issue,\u201d but the \u201creal status\u201d part is tricky. Engineering doesn\u2019t like committing to ETAs.",
"link": "https://www.onyx.app/4475787-16"
},
{
"text": "Arjun Mehta: Right, and Issue_Tracker doesn\u2019t force hard ETAs. It can capture status notes and decision history in a way that\u2019s searchable and attributable\u2014so if Engineering says \u201cinvestigating root cause; workaround exists,\u201d that\u2019s what gets surfaced, with the supporting tickets linked. It helps Sales and Support avoid guessing. And because clusters are deduped, you don\u2019t have 50 tickets all asking for updates separately\u2014Support can update one cluster and it propagates contextually.",
"link": "https://www.onyx.app/4475787-17"
},
{
"text": "Jade Jacobs: Okay. But I\u2019m not sure this is a priority for us this quarter. We\u2019re in the middle of a tooling freeze.",
"link": "https://www.onyx.app/4475787-18"
},
{
"text": "Arjun Mehta: I hear you. If this quarter is locked, it may not be the right time. The only reason I reached out is I\u2019ve seen snack/CPG brands with ecommerce + wholesale pipelines get hit with deal slippage when a recurring issue pops up\u2014like order routing bugs, integration failures, or reporting inaccuracies\u2014and Sales doesn\u2019t have a crisp, approved status to share. Even one late-stage deal slipping can be painful.",
"link": "https://www.onyx.app/4475787-19"
},
{
"text": "Jade Jacobs: We\u2019re more B2B wholesale, and our issues are usually operational rather than software defects. Also, we don\u2019t run Intercom\u2014just Zendesk.",
"link": "https://www.onyx.app/4475787-20"
},
{
"text": "Arjun Mehta: Zendesk alone is fine. And operational issues can still be captured as themes if they\u2019re coming through support channels\u2014like \u201cinvoice mismatch\u201d or \u201cASN delays\u201d\u2014and then tied back to impacted accounts. But if most of that work happens outside ticketing, Issue_Tracker may not be as strong a fit.",
"link": "https://www.onyx.app/4475787-21"
},
{
"text": "Jade Jacobs: Yeah, a lot of that is handled by our ops team directly. Support tickets exist, but they\u2019re not the whole picture.",
"link": "https://www.onyx.app/4475787-22"
},
{
"text": "Arjun Mehta: That\u2019s helpful. In that case, it might be less of an immediate win. One lightweight way some teams start is simply using the Zendesk + Jira sync and dedupe clustering to reduce duplicate engineering asks\u2014without trying to model every operational workflow. But if Engineering volume is low, it\u2019s probably not compelling.",
"link": "https://www.onyx.app/4475787-23"
},
{
"text": "Jade Jacobs: Engineering volume is relatively low, and Jira is mostly for internal projects. We don\u2019t open many tickets from Support.",
"link": "https://www.onyx.app/4475787-24"
},
{
"text": "Arjun Mehta: Got it. Then I don\u2019t want to force a square peg here. Would it be useful if I sent a one-pager on the \u201clate-stage deal risk\u201d use case\u2014how linking Salesforce opportunities to issue clusters works\u2014just so you have it on file in case priorities shift later?",
"link": "https://www.onyx.app/4475787-25"
},
{
"text": "Jade Jacobs: You can send it, sure, but I can\u2019t promise I\u2019ll get to it. And I don\u2019t think I can commit to a longer sales call right now.",
"link": "https://www.onyx.app/4475787-26"
},
{
"text": "Arjun Mehta: Completely fine. Last quick question so I don\u2019t waste your inbox: when Sales does get pulled into an issue\u2014say a key account escalation\u2014where do they go today to understand status? Is it Slack, a CSM, a spreadsheet?",
"link": "https://www.onyx.app/4475787-27"
},
{
"text": "Jade Jacobs: Usually they ping the CSM or Support lead, and then someone asks Engineering if needed. It\u2019s messy, but it\u2019s workable.",
"link": "https://www.onyx.app/4475787-28"
},
{
"text": "Arjun Mehta: Understood. That \u201cmessy but workable\u201d is exactly the gap we address\u2014when it becomes frequent enough to justify standardizing. I\u2019ll send the short overview and a couple screenshots of the Zendesk-to-cluster-to-Jira back-link flow and the Salesforce opportunity linkage. If at some point you want to sanity-check fit, we can do a 20-minute discovery\u2014no prep\u2014just to confirm whether it\u2019s worth revisiting.",
"link": "https://www.onyx.app/4475787-29"
},
{
"text": "Jade Jacobs: Okay. Send it over and maybe we\u2019ll circle back later in the year.",
"link": "https://www.onyx.app/4475787-30"
},
{
"text": "Arjun Mehta: Will do. Thanks again for the time, Jade\u2014appreciate it. I\u2019ll keep it concise in email and won\u2019t spam you. Have a good rest of your day.",
"link": "https://www.onyx.app/4475787-31"
},
{
"text": "Jade Jacobs: Thanks. Bye.",
"link": "https://www.onyx.app/4475787-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "arjun_mehta@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "jade_jacobs@nimbunest_snacks.onyx.app"
}
]
}

View File

@@ -0,0 +1,161 @@
{
"id": "FIREFLIES_1707555",
"semantic_identifier": "1707555 - Timberline Metro Transit - sales_pitch - Discussion with Rhea Narang",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-03 00:00:00",
"metadata": {
"meeting_date": "2025-06-03 00:00:00",
"duration_min": "40"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "1707555 - Timberline Metro Transit - sales_pitch - Discussion with Rhea Narang",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hi Rhea\u2014James Choi here. Thanks for taking a few minutes. I\u2019ll keep it tight. I\u2019m calling because we built Customer_Analyzer to help teams like yours get really clear on which customer segments are actually your best fit\u2014and then use that to prioritize roadmap and messaging. Does that map to anything you\u2019re working on at Timberline Metro Transit right now?",
"link": "https://www.onyx.app/2155036-1"
},
{
"text": "Rhea Narang: Hi James. We\u2019re in the middle of a few initiatives, but I\u2019m not sure we\u2019re looking for new tooling at the moment. What exactly does it do?",
"link": "https://www.onyx.app/2155036-2"
},
{
"text": "James Choi: Totally fair. In one sentence: we ingest the customer signals you already have\u2014CRM fields, onboarding surveys, product usage events, support tickets, even interview notes\u2014and we turn it into a fit score from 0 to 100 at the account and segment level, plus a breakdown of why. The practical outcome is you can see, for example, \u201cSegment A renews and expands, Segment B churns,\u201d and which drivers are behind that.",
"link": "https://www.onyx.app/2155036-3"
},
{
"text": "Rhea Narang: Okay. We already have reporting in our CRM and some dashboards. How is this different from analytics we can build internally?",
"link": "https://www.onyx.app/2155036-4"
},
{
"text": "James Choi: Great question. The difference is it\u2019s not just reporting\u2014it\u2019s fit estimation that\u2019s oriented around outcomes. You define fit drivers that matter to you\u2014industry or size, problem severity, willingness to pay, time-to-value, adoption patterns, support burden, churn risk signals\u2014then we score accounts and segments and show a driver-level explanation. So rather than \u201cusage is down,\u201d it becomes \u201cthis segment\u2019s support burden and time-to-value make them low-fit, and that\u2019s correlated with churn.\u201d It\u2019s meant to help teams align on an ICP and make sharper calls.",
"link": "https://www.onyx.app/2155036-5"
},
{
"text": "Rhea Narang: I get the concept. But we\u2019re a transit organization\u2014our \u201ccustomers\u201d are a mix of internal stakeholders, riders, partners. I\u2019m not sure your model applies cleanly.",
"link": "https://www.onyx.app/2155036-6"
},
{
"text": "James Choi: That makes sense, and it\u2019s a common question. When I say \u201ccustomer,\u201d it can be any account or segment you\u2019re trying to serve consistently\u2014partners, agencies, departments, even rider cohorts if you have the signals. The use case I see most for teams like yours is product planning: identifying the high-fit segments with unmet needs, so roadmap bets are driven by the groups most likely to adopt and stick.",
"link": "https://www.onyx.app/2155036-7"
},
{
"text": "Rhea Narang: We do product planning, but our constraints are often policy and budget cycles. I\u2019m not sure a fit score changes that.",
"link": "https://www.onyx.app/2155036-8"
},
{
"text": "James Choi: Understood. It may not change constraints, but it can help you decide where you invest within them. For instance, if you\u2019re choosing between two initiatives, the fit estimation can show which segment will reach value faster and require less support burden\u2014so you\u2019re not unintentionally prioritizing segments that look loud but are historically low adoption or high churn risk.",
"link": "https://www.onyx.app/2155036-9"
},
{
"text": "Rhea Narang: We don\u2019t really think in terms of churn, and support burden is hard to quantify for us. Also, we have limited engineering bandwidth to integrate anything new.",
"link": "https://www.onyx.app/2155036-10"
},
{
"text": "James Choi: On integration, we try to keep it light. A lot of teams start with just CRM exports plus onboarding or survey data, and optionally support tickets. You can add product usage events later if that\u2019s available. On support burden, we can proxy it from ticket volumes, categories, time-to-resolution, or even qualitative tags from notes\u2014doesn\u2019t have to be perfect to be directional.",
"link": "https://www.onyx.app/2155036-11"
},
{
"text": "Rhea Narang: But we\u2019d still have to clean and map all that data. And I\u2019m not convinced we\u2019d trust an automated scoring model enough to base decisions on it.",
"link": "https://www.onyx.app/2155036-12"
},
{
"text": "James Choi: That\u2019s fair. The way we address trust is with the driver breakdown. It\u2019s not a black box \u201cthis is your ICP.\u201d It shows why a segment scored high or low\u2014like time-to-value, adoption, ticket volume\u2014so your team can sanity-check it. If you disagree with a driver, you can adjust weights or definitions and see the impact.",
"link": "https://www.onyx.app/2155036-13"
},
{
"text": "Rhea Narang: That sounds like a lot of tuning. Honestly, we have a pretty good sense of who our priority segments are already, even if it\u2019s not quantified.",
"link": "https://www.onyx.app/2155036-14"
},
{
"text": "James Choi: If you already have strong intuition, Customer_Analyzer can help validate it quickly\u2014and sometimes reveal edge cases. For example, a segment you assume is high value might convert, but require outsized support and never expands; while another segment quietly renews with low effort. The tool helps make those trade-offs explicit for planning.",
"link": "https://www.onyx.app/2155036-15"
},
{
"text": "Rhea Narang: I\u2019m not saying it\u2019s useless. I just don\u2019t have a current project where I could justify evaluating and implementing this. We\u2019re also in a procurement freeze for new SaaS for at least this quarter.",
"link": "https://www.onyx.app/2155036-16"
},
{
"text": "James Choi: Got it\u2014thanks for being direct. In that case, would it be helpful if I sent a one-pager focused on the product planning use case\u2014how teams use fit estimation to identify high-fit customers with unmet needs\u2014so you have it if priorities shift?",
"link": "https://www.onyx.app/2155036-17"
},
{
"text": "Rhea Narang: You can send it, but I can\u2019t promise I\u2019ll get to it soon.",
"link": "https://www.onyx.app/2155036-18"
},
{
"text": "James Choi: Totally understood. Before I do\u2014quick check: who usually owns ICP or segment prioritization on your side? Is that product, strategy, or someone in analytics?",
"link": "https://www.onyx.app/2155036-19"
},
{
"text": "Rhea Narang: It\u2019s shared. Product has input, but strategy and operations are heavily involved. Analytics supports when we can.",
"link": "https://www.onyx.app/2155036-20"
},
{
"text": "James Choi: That helps. If you think it\u2019s appropriate, I can address the one-pager to that broader group and keep it non-salesy\u2014more \u201chere\u2019s a framework and what data signals it uses.\u201d And if later you want to explore, we can do a short working session rather than a full pitch.",
"link": "https://www.onyx.app/2155036-21"
},
{
"text": "Rhea Narang: Maybe. But again, we can\u2019t buy tools right now, and I\u2019m hesitant to pull more people into something we can\u2019t act on.",
"link": "https://www.onyx.app/2155036-22"
},
{
"text": "James Choi: Completely fair. Then I\u2019ll keep it simple: I\u2019ll send a concise overview and a sample fit driver list\u2014things like time-to-value proxies, adoption indicators, and support load\u2014so you can see whether it matches your environment. No follow-up cadence unless you reply.",
"link": "https://www.onyx.app/2155036-23"
},
{
"text": "Rhea Narang: Okay, that\u2019s fine. Send it over.",
"link": "https://www.onyx.app/2155036-24"
},
{
"text": "James Choi: Will do. Last quick question so I tailor it: do you have any consistent sources like ticketing, surveys, or a CRM-like system for partner engagements, or is it mostly ad hoc?",
"link": "https://www.onyx.app/2155036-25"
},
{
"text": "Rhea Narang: We have a ticketing system and some survey data, but it\u2019s fragmented. And we\u2019re not looking to connect more systems right now.",
"link": "https://www.onyx.app/2155036-26"
},
{
"text": "James Choi: Understood. I\u2019ll frame it as \u201cstart with what you have,\u201d and I\u2019ll include an example of using just tickets and survey signals to estimate fit at a segment level. Thanks again for the time, Rhea.",
"link": "https://www.onyx.app/2155036-27"
},
{
"text": "Rhea Narang: Thanks, James. I\u2019ll look when I can.",
"link": "https://www.onyx.app/2155036-28"
},
{
"text": "James Choi: Appreciate it. I\u2019ll send that shortly. Have a good rest of your day.",
"link": "https://www.onyx.app/2155036-29"
},
{
"text": "Rhea Narang: You too.",
"link": "https://www.onyx.app/2155036-30"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "rhea_narang@timberline_metro_transit.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_3246639",
"semantic_identifier": "3246639 - Marinerock Outdoor Gear - sales_call - Discussion with Dustin Dean",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-02 00:00:00",
"metadata": {
"meeting_date": "2025-06-02 00:00:00",
"duration_min": "19"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "3246639 - Marinerock Outdoor Gear - sales_call - Discussion with Dustin Dean",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hey Dustin, good to see you again. Thanks for making the time. Last call you mentioned you\u2019re trying to tighten quarter-end forecasting and reduce the scramble with spreadsheet rollups. Is that still the top priority?",
"link": "https://www.onyx.app/3591754-1"
},
{
"text": "Dustin Dean: Hey James\u2014yeah, definitely. We\u2019re still doing a lot of manual stitching together from Salesforce, and it gets messy fast. The hardest part is figuring out which \u201ccommit\u201d deals are actually solid.",
"link": "https://www.onyx.app/3591754-2"
},
{
"text": "James Choi: That\u2019s exactly where Sales_Accelerator is strongest. Since you already have Salesforce as the source of truth, we sit on top of it\u2014no rip-and-replace\u2014and we generate an explainable forecast at the opportunity and pipeline level. The big shift is it doesn\u2019t just look at stage; it blends in activity signals like last touch, meetings scheduled, and how long the deal\u2019s been sitting in-stage, and then shows you the drivers behind the number.",
"link": "https://www.onyx.app/3591754-3"
},
{
"text": "Dustin Dean: When you say \u201cexplainable,\u201d what does that look like? Because I\u2019ve seen forecasting tools that give you a score and everyone immediately distrusts it.",
"link": "https://www.onyx.app/3591754-4"
},
{
"text": "James Choi: Totally fair. In Sales_Accelerator, every forecast view\u2014commit, best case, pipeline\u2014can be drilled into so a manager can answer \u201cwhy is this deal weakening?\u201d You\u2019d see, for example: stage age is above your benchmark, there\u2019s no next meeting on the calendar, email activity dropped in the last two weeks, and for deals like this historically your win rate is X. It\u2019s not a black box score; it\u2019s a set of visible drivers you can act on in a deal review.",
"link": "https://www.onyx.app/3591754-5"
},
{
"text": "Dustin Dean: Okay, that\u2019s interesting. We do have a problem where reps keep deals in commit because they \u201cfeel good,\u201d and then two weeks later we realize there\u2019s no next step.",
"link": "https://www.onyx.app/3591754-6"
},
{
"text": "James Choi: Exactly. So one common workflow for a VP Sales is: on Monday, you pull your commit forecast, then filter for \u201ccommit deals showing risk signals.\u201d Risk signals are things like stage aging beyond your norm, no next meeting scheduled, low engagement, or slipping close dates. Instead of debating every deal, you run targeted deal reviews on the few that are statistically weakening.",
"link": "https://www.onyx.app/3591754-7"
},
{
"text": "Dustin Dean: That would save time. But how does it handle differences across segments? Our wholesale deals behave differently than our direct-to-consumer partnerships.",
"link": "https://www.onyx.app/3591754-8"
},
{
"text": "James Choi: Great point. Forecasting can be sliced by segment, rep, and source. And the benchmarks\u2014like typical cycle time and stage conversion\u2014can be learned separately per segment. So wholesale doesn\u2019t get judged by the same \u201cnormal\u201d as a smaller partnership motion. That\u2019s also where the \u201cdrivers\u201d stay credible because it\u2019s comparing like-to-like historical patterns.",
"link": "https://www.onyx.app/3591754-9"
},
{
"text": "Dustin Dean: Makes sense. We also talked last time about our ICP work. We\u2019ve been using Customer_Analyzer for fit scoring\u2014how does your product relate to that?",
"link": "https://www.onyx.app/3591754-10"
},
{
"text": "James Choi: Perfect segue. Sales_Accelerator integrates with Customer_Analyzer rather than duplicating it. If you have fit tiers or fit scores coming out of Customer_Analyzer, Sales_Accelerator can use that as a weighting factor in forecast and prioritization. So if two deals look identical in stage and activity but one is ICP Tier 1 and the other is Tier 3, you\u2019ll see that reflected in both forecast confidence and where managers focus attention.",
"link": "https://www.onyx.app/3591754-11"
},
{
"text": "Dustin Dean: I like the idea, but I worry reps will push back if they feel like the tool is \u201coverruling\u201d their judgment. Especially if it starts telling them their commit deal isn\u2019t commit.",
"link": "https://www.onyx.app/3591754-12"
},
{
"text": "James Choi: That\u2019s a healthy concern, and we see it a lot early on. The way teams get adoption is by positioning it as \u201cbetter deal inspection,\u201d not \u201cthe tool is the boss.\u201d In practice, you use it to ask better questions: \u201cI see we\u2019re in commit but there\u2019s no next meeting\u2014what\u2019s the agreed next step?\u201d Or \u201cWe\u2019re past the normal stage time\u2014what\u2019s changed?\u201d The rep can absolutely override, but the tool forces the conversation onto facts\u2014stage age, engagement, next meeting\u2014so it\u2019s constructive rather than personal.",
"link": "https://www.onyx.app/3591754-13"
},
{
"text": "Dustin Dean: That would help. Our deal reviews sometimes become gut-feel debates.",
"link": "https://www.onyx.app/3591754-14"
},
{
"text": "James Choi: Exactly, and that\u2019s where the quarter-end scramble comes from. Another angle: scenario planning. So you can do \u201cwhat if\u201d planning\u2014like, \u201cIf we add 20 SQLs in our ICP Tier 1 segment, what\u2019s the expected impact on pipeline coverage or bookings this quarter?\u201d It\u2019s not just a static forecast; it helps you pressure-test assumptions and see what levers actually move the number.",
"link": "https://www.onyx.app/3591754-15"
},
{
"text": "Dustin Dean: Scenario planning is appealing, especially when marketing asks what we need to hit the quarter. But again, I\u2019m going to come back to trust\u2014how quickly can we validate it\u2019s not garbage-in, garbage-out from Salesforce?",
"link": "https://www.onyx.app/3591754-16"
},
{
"text": "James Choi: You\u2019re right to anchor on that. We typically start with a short baseline phase: connect Salesforce, pull historical opps, and generate stage conversion and cycle-time benchmarks. Then we compare Sales_Accelerator\u2019s forecast against your last few quarters and current pipeline to calibrate. If you have gaps\u2014like missing activity logging or inconsistent stage definitions\u2014we\u2019ll surface that fast because the drivers will look off. The goal is to make the quality issues visible, not hide them.",
"link": "https://www.onyx.app/3591754-17"
},
{
"text": "Dustin Dean: Activity logging is a mixed bag here. Some reps are disciplined, some aren\u2019t. Does the tool fall apart if logging isn\u2019t perfect?",
"link": "https://www.onyx.app/3591754-18"
},
{
"text": "James Choi: It doesn\u2019t require perfection, but it does get stronger with consistency. If activity is sparse, the model leans more on stage age, close date movement, and historical outcomes for similar deals\u2014and it will show that in the drivers. And practically, a lot of teams use this as a gentle nudge: when reps see that \u201cno next meeting\u201d is making their deals show up as risk, they start scheduling and logging the next step because it directly impacts how leadership views the deal.",
"link": "https://www.onyx.app/3591754-19"
},
{
"text": "Dustin Dean: That\u2019s actually a good behavior change. I can see our managers using that in a consistent way.",
"link": "https://www.onyx.app/3591754-20"
},
{
"text": "James Choi: Exactly. And because it\u2019s explainable, managers aren\u2019t saying \u201cthe AI says you\u2019re wrong.\u201d They\u2019re saying \u201cwe\u2019re missing a next step; let\u2019s fix it.\u201d That usually lands better.",
"link": "https://www.onyx.app/3591754-21"
},
{
"text": "Dustin Dean: So if we were to move forward, what would you suggest as the first use case to prove value quickly?",
"link": "https://www.onyx.app/3591754-22"
},
{
"text": "James Choi: For Marinerock, I\u2019d recommend we start with the VP Sales quarter-end accuracy use case: replace the spreadsheet rollups with the explainable forecast and a \u201ccommit risk\u201d view for deal reviews. It\u2019s a clean before/after: time saved, fewer surprises, and better visibility into which commit deals are weakening and why. Once that\u2019s working, you can expand into deeper guidance, but forecasting is the fastest path to measurable ROI.",
"link": "https://www.onyx.app/3591754-23"
},
{
"text": "Dustin Dean: That aligns. Our VP of Sales is the one pushing for this, and the spreadsheet pain is real. What would implementation look like in terms of time and resources?",
"link": "https://www.onyx.app/3591754-24"
},
{
"text": "James Choi: Lightweight to start. Since we sit on Salesforce, we\u2019re not changing your process; we\u2019re reading it and layering analytics and guidance. Typically: week one is integration and data mapping, week two is calibration and validation with your sales ops lead, and then we do a manager enablement session focused on using the drivers in deal reviews. You can be live for a pilot inside a month, often sooner depending on access and data hygiene.",
"link": "https://www.onyx.app/3591754-25"
},
{
"text": "Dustin Dean: Okay. And pricing aside for a second\u2014how would you run a pilot so it doesn\u2019t become \u201canother dashboard nobody opens\u201d?",
"link": "https://www.onyx.app/3591754-26"
},
{
"text": "James Choi: We make it workflow-based. For the pilot, we pick one segment or one leadership cadence\u2014like your weekly commit call. Success criteria could be: eliminate the manual rollup, use Sales_Accelerator forecast as the default view, and require that every commit deal has an identified next step. We\u2019ll track how many \u201ccommit risk\u201d deals get addressed and whether slippage decreases. If it\u2019s not being used in the meeting rhythm, it won\u2019t stick\u2014so we design it around that rhythm.",
"link": "https://www.onyx.app/3591754-27"
},
{
"text": "Dustin Dean: That\u2019s good. I can already picture using it in our Monday forecast meeting. If it can reliably flag the \u201cfake commit\u201d deals early, that\u2019s a win.",
"link": "https://www.onyx.app/3591754-28"
},
{
"text": "James Choi: Great. Here\u2019s what I\u2019d propose as next steps to get you to a buying decision: I\u2019ll send a one-page pilot plan\u2014scope, timeline, success metrics\u2014and we schedule a 30-minute session with your VP Sales and sales ops to confirm the data sources and the segment split. If they\u2019re aligned, we can move to a formal proposal right after. Does that work?",
"link": "https://www.onyx.app/3591754-29"
},
{
"text": "Dustin Dean: Yes, let\u2019s do that. Include our VP Sales and our sales ops manager. If they\u2019re comfortable with the explainability and the rollout plan, I think we can move quickly.",
"link": "https://www.onyx.app/3591754-30"
},
{
"text": "James Choi: Perfect. I\u2019ll follow up today with times for early next week and the pilot plan. And Dustin\u2014before we wrap\u2014any other concern you want me to address proactively for them? Usually it\u2019s trust, data quality, or \u201cwill reps hate this.\u201d",
"link": "https://www.onyx.app/3591754-31"
},
{
"text": "Dustin Dean: Trust and adoption are the big ones. If you can come prepared with how other teams introduced it without backlash, that would help.",
"link": "https://www.onyx.app/3591754-32"
},
{
"text": "James Choi: Absolutely. I\u2019ll bring a couple of adoption playbooks and examples of the driver-based deal review questions managers use, so it feels practical and not theoretical. Thanks, Dustin\u2014talk soon.",
"link": "https://www.onyx.app/3591754-33"
},
{
"text": "Dustin Dean: Sounds good, James. Thanks.",
"link": "https://www.onyx.app/3591754-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "dustin_dean@marinerock_outdoor_gear.onyx.app"
}
]
}

View File

@@ -0,0 +1,161 @@
{
"id": "FIREFLIES_4210674",
"semantic_identifier": "4210674 - Pacific Ember Foods - sales_pitch - Discussion with Kent Delgado",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-13 00:00:00",
"metadata": {
"meeting_date": "2025-06-13 00:00:00",
"duration_min": "17"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "4210674 - Pacific Ember Foods - sales_pitch - Discussion with Kent Delgado",
"organizer_email": "irene_shen@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Irene Shen: Hi Kent\u2014this is Irene Shen. Thanks for taking a few minutes. I work with teams who are living in Salesforce or HubSpot but still struggling with \u201cwhat do I do next\u201d on each lead and opportunity. I wanted to quickly share how Sales_Accelerator helps with that and see if it\u2019s worth a deeper session.",
"link": "https://www.onyx.app/9445859-1"
},
{
"text": "Kent Delgado: Hi Irene. Yeah, I\u2019ve got about five minutes. What is it exactly\u2014another CRM?",
"link": "https://www.onyx.app/9445859-2"
},
{
"text": "Irene Shen: Totally fair question. It\u2019s not a CRM replacement. Sales_Accelerator sits on top of your existing CRM as an execution layer. It looks at what\u2019s happening across your pipeline and then does two things really well: it recommends the best next action for each deal\u2014inside the CRM\u2014and it flags risk early when deals are likely to slip. It also plugs into your existing customer fit/ICP work rather than duplicating it.",
"link": "https://www.onyx.app/9445859-3"
},
{
"text": "Kent Delgado: Okay. We\u2019re using HubSpot today, and our reps already have sequences. What\u2019s different here?",
"link": "https://www.onyx.app/9445859-4"
},
{
"text": "Irene Shen: Sequences are great for consistent outreach, but they\u2019re usually \u201cone-size-fits-most.\u201d Where Sales_Accelerator helps is: it adapts the next step based on what\u2019s actually happening in the opportunity. For example, if a meeting happened and there\u2019s no follow-up logged, it\u2019ll prompt a recap email with next steps. If only one stakeholder is engaged, it recommends multi-threading and suggests who to pull in. If the mutual plan is incomplete, it generates a lightweight checklist and reminders so deals don\u2019t stall quietly.",
"link": "https://www.onyx.app/9445859-5"
},
{
"text": "Kent Delgado: That sounds useful, but we\u2019re a food company\u2014our process isn\u2019t super enterprise-y. We mostly care about inbound speed-to-lead and not letting good leads rot.",
"link": "https://www.onyx.app/9445859-6"
},
{
"text": "Irene Shen: Perfect\u2014one of the strongest use cases is aligning speed-to-lead for inbound. Sales_Accelerator prioritizes high-fit inbound leads, recommends the best first response message, and then tracks whether faster first-touch is actually correlating with higher win rate. So instead of a dashboard that says \u201crespond faster,\u201d reps get a queue: \u201cThese five inbound leads are high fit\u2014here\u2019s the best first-touch message, here\u2019s the channel, and here\u2019s the recommended cadence for the next 48 hours.\u201d",
"link": "https://www.onyx.app/9445859-7"
},
{
"text": "Kent Delgado: How does it know \u201chigh fit\u201d? We\u2019ve got some scoring, but it\u2019s not great.",
"link": "https://www.onyx.app/9445859-8"
},
{
"text": "Irene Shen: If you already have scoring, we can consume it. If you\u2019re using something like Customer_Analyzer\u2014or even if you\u2019re not today\u2014we can ingest fit scores and positioning blocks so reps aren\u2019t guessing. The key is: Sales_Accelerator doesn\u2019t redo your ICP work; it operationalizes it. It takes \u201cthis lead is a fit and here\u2019s the positioning\u201d and turns it into: \u201csend this message, ask these questions, and here\u2019s the next action if they raise objection X.\u201d",
"link": "https://www.onyx.app/9445859-9"
},
{
"text": "Kent Delgado: We don\u2019t use Customer_Analyzer specifically. We do have some segments, and the team has talk tracks. Would your tool still work?",
"link": "https://www.onyx.app/9445859-10"
},
{
"text": "Irene Shen: Yes. We can start with your existing segments and talk tracks and layer in smarter guidance over time. The first win tends to be operational: reps don\u2019t have to decide what to do next, managers don\u2019t have to chase basic hygiene, and you get a measurable improvement in inbound follow-up consistency.",
"link": "https://www.onyx.app/9445859-11"
},
{
"text": "Kent Delgado: What does \u201cguidance\u201d look like\u2014are reps getting spammed with alerts?",
"link": "https://www.onyx.app/9445859-12"
},
{
"text": "Irene Shen: No, we\u2019re careful about that. It\u2019s primarily a task queue and in-context recommendations on the record. For example: \u201cOpportunity at stage 2 has one contact engaged, last activity 8 days ago, no next meeting scheduled\u2014recommended next actions: 1) send recap + propose times, 2) add operations contact, 3) update mutual action plan.\u201d Reps can one-click create the tasks or messages, and managers can see where the pipeline is drifting without a bunch of manual inspection.",
"link": "https://www.onyx.app/9445859-13"
},
{
"text": "Kent Delgado: And this lives in HubSpot?",
"link": "https://www.onyx.app/9445859-14"
},
{
"text": "Irene Shen: Exactly\u2014either embedded in the CRM or via a lightweight extension, depending on your setup. The idea is not to add a new tab reps have to remember. It\u2019s \u201cwhere they already work.\u201d",
"link": "https://www.onyx.app/9445859-15"
},
{
"text": "Kent Delgado: Got it. What\u2019s the risk flagging you mentioned?",
"link": "https://www.onyx.app/9445859-16"
},
{
"text": "Irene Shen: We look for patterns that precede slippage: stalled stages, missing stakeholders, lack of next step, mutual plan gaps, inconsistent activity, that kind of thing. Then we surface \u201cdeal at risk\u201d with the specific reason and the recommended fix. That\u2019s really helpful for forecasting conversations because instead of arguing about gut feel, you\u2019re looking at explainable signals.",
"link": "https://www.onyx.app/9445859-17"
},
{
"text": "Kent Delgado: Interesting. My worry is change management. Our team is small, and if this feels heavy, it won\u2019t stick.",
"link": "https://www.onyx.app/9445859-18"
},
{
"text": "Irene Shen: That\u2019s a good callout. This is designed to be lightweight\u2014think \u201cnudges and consistency\u201d rather than a forced methodology. The initial rollout can be just one motion: inbound speed-to-lead. We\u2019d set up the prioritization and first-touch recommendations, measure response time and conversion impact, and only then expand to deal progression recommendations for later stages.",
"link": "https://www.onyx.app/9445859-19"
},
{
"text": "Kent Delgado: If we did that, what would you need from us?",
"link": "https://www.onyx.app/9445859-20"
},
{
"text": "Irene Shen: For the inbound pilot: access to HubSpot objects for leads/contacts, lifecycle stages, activity logs, and your current routing rules and sequences. Then we\u2019d define what \u201chigh fit\u201d means using your existing segments or scoring, and we\u2019d map a couple of first-response templates per segment. Usually we can stand up something usable in a couple of weeks.",
"link": "https://www.onyx.app/9445859-21"
},
{
"text": "Kent Delgado: And you can show this before we commit to anything?",
"link": "https://www.onyx.app/9445859-22"
},
{
"text": "Irene Shen: Absolutely. What I\u2019d suggest is a 30-minute working session\u2014screen share\u2014where we walk through your current inbound flow and then I\u2019ll demo what the rep experience looks like: the prioritized queue, the recommended first response, and an example of the follow-up recommendation when a lead engages but no next step is set.",
"link": "https://www.onyx.app/9445859-23"
},
{
"text": "Kent Delgado: That would be helpful. I\u2019d want our sales ops person to see it too, since they own HubSpot.",
"link": "https://www.onyx.app/9445859-24"
},
{
"text": "Irene Shen: Perfect. If you bring sales ops, we can also cover how the recommendations are configured and what reporting you get\u2014like speed-to-lead by fit tier and the win-rate lift. Does early next week work?",
"link": "https://www.onyx.app/9445859-25"
},
{
"text": "Kent Delgado: Yeah, early next week is fine. Tuesday afternoon maybe.",
"link": "https://www.onyx.app/9445859-26"
},
{
"text": "Irene Shen: Great\u2014how about Tuesday at 2:00 PM Pacific for 30 minutes? If that\u2019s tight, we can do 45 and include a few specific scenarios from Pacific Ember Foods\u2014like your top inbound sources and what happens after the first reply.",
"link": "https://www.onyx.app/9445859-27"
},
{
"text": "Kent Delgado: Let\u2019s do 2:00 PM Pacific for 45 minutes. I\u2019ll loop in ops.",
"link": "https://www.onyx.app/9445859-28"
},
{
"text": "Irene Shen: Awesome. I\u2019ll send a calendar invite for Tuesday 2:00 PM Pacific, 45 minutes, and I\u2019ll include a short list of what we\u2019ll cover and the minimal HubSpot info that\u2019s helpful. Thanks, Kent\u2014looking forward to it.",
"link": "https://www.onyx.app/9445859-29"
},
{
"text": "Kent Delgado: Sounds good. Thanks, Irene.",
"link": "https://www.onyx.app/9445859-30"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "irene_shen@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "kent_delgado@pacific_ember_foods.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_4864163",
"semantic_identifier": "4864163 - Unitypeak Nonprofits - sales_call - Discussion with Fernando Chapman",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-17 00:00:00",
"metadata": {
"meeting_date": "2025-06-17 00:00:00",
"duration_min": "40"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "4864163 - Unitypeak Nonprofits - sales_call - Discussion with Fernando Chapman",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi Fernando, thanks for making the time again. Last time you mentioned Unitypeak\u2019s biggest pain is the end-of-month \u201csurprise\u201d on pipeline\u2014things look fine in the CRM and then three deals slip at once. I wanted to focus today on one piece of Sales_Accelerator that directly targets that: our sales_forecast view and the drivers behind it. Does that still feel like the priority?",
"link": "https://www.onyx.app/6774703-1"
},
{
"text": "Fernando Chapman: Yeah, forecast accuracy is still a priority. But I\u2019m also cautious\u2014our CRM data isn\u2019t always perfect, and I don\u2019t want another dashboard that\u2019s only as good as what people remember to enter.",
"link": "https://www.onyx.app/6774703-2"
},
{
"text": "Megan Foster: Totally fair, and that\u2019s actually the right lens for this. Sales_Accelerator sits on top of your CRM, so we don\u2019t try to replace Salesforce discipline\u2014we surface where the discipline is breaking the forecast. The difference from a standard CRM forecast is we combine stage with activity signals\u2014email/call/meeting patterns\u2014and cycle-time benchmarks, and we can optionally use Customer_Analyzer\u2019s fit score as a weighting factor. So if a deal is \u201clate\u201d for the stage and hasn\u2019t had a meaningful touch, it drops out of commit even if it\u2019s still marked \u201cverbal yes.\u201d",
"link": "https://www.onyx.app/6774703-3"
},
{
"text": "Fernando Chapman: That sounds helpful in theory. But we\u2019ve tried \u201cactivity-based\u201d tools before. The reps complained about false alerts and it became noise. And honestly, our stages are inconsistent across teams\u2014some folks skip stages, some don\u2019t update close dates until the last second.",
"link": "https://www.onyx.app/6774703-4"
},
{
"text": "Megan Foster: Understood. Let me anchor on how it\u2019s explainable so it\u2019s not a black box. For any forecast number\u2014commit, best-case, pipeline\u2014you can click into the drivers: stage age, last touch, whether a next meeting is scheduled, fit tier if you bring in Customer_Analyzer, and historical win rate for similar deals. Then you can see \u201cwhy\u201d it\u2019s at risk, not just \u201cit\u2019s at risk.\u201d It\u2019s less \u201calerts everywhere\u201d and more \u201cthese five deals are the reason your commit is inflated.\u201d",
"link": "https://www.onyx.app/6774703-5"
},
{
"text": "Fernando Chapman: I get the value of explainable drivers. But if our close dates and next steps aren\u2019t reliable, won\u2019t your \u201cstage age\u201d and \u201cnext meeting scheduled\u201d driver just reflect bad inputs? We\u2019ve fought that battle for years.",
"link": "https://www.onyx.app/6774703-6"
},
{
"text": "Megan Foster: You\u2019re right that hygiene matters. Where teams see traction quickly is using the system to *highlight* hygiene problems in a way that\u2019s tied to outcomes. For example, Sales ops can view stage slippage diagnostics and conversion by segment and rep. If one segment shows a big drop from discovery to evaluation, we can recommend a process fix\u2014like moving security review earlier\u2014and then monitor whether conversion improves. The tool becomes a feedback loop, not just a report.",
"link": "https://www.onyx.app/6774703-7"
},
{
"text": "Fernando Chapman: We\u2019re a nonprofit org. Our \u201csegments\u201d aren\u2019t clean like enterprise SaaS. We have donors, partners, institutional grants, and different cycles. I\u2019m not sure benchmarking would be meaningful.",
"link": "https://www.onyx.app/6774703-8"
},
{
"text": "Megan Foster: That\u2019s fair. The benchmarks don\u2019t have to be generic industry averages. We base them on *your* historical cycle times and win rates by whatever breakdown is meaningful\u2014source, motion, rep, or even \u201cgrant-funded vs. donor-led.\u201d The system learns patterns from your own pipeline history. And on top of that, the scenario planning can help you quantify what you need\u2014like \u201cIf we add 20 SQLs in the highest-fit tier, what\u2019s the expected impact this quarter?\u201d That\u2019s especially useful for planning around board expectations.",
"link": "https://www.onyx.app/6774703-9"
},
{
"text": "Fernando Chapman: Scenario planning is interesting. But again, it assumes the underlying data is accurate. Our reps are not great at logging emails and meetings either. Some of it is in Gmail, some in calendar, and some just\u2026 not tracked.",
"link": "https://www.onyx.app/6774703-10"
},
{
"text": "Megan Foster: That\u2019s the other side of \u201cactivity signals.\u201d If you connect email/calendar, we can pull signals without reps logging every touch manually. It\u2019s not perfect, but it reduces reliance on manual entry. And we can set thresholds so it\u2019s not pinging people for minor things\u2014more like \u201cno touch in 14 days on a late-stage deal\u201d rather than \u201cyou didn\u2019t send an email today.\u201d",
"link": "https://www.onyx.app/6774703-11"
},
{
"text": "Fernando Chapman: We\u2019d have to get IT and security comfortable with connecting email and calendars. That can take months here. And I\u2019m also wary that if reps feel monitored, adoption drops.",
"link": "https://www.onyx.app/6774703-12"
},
{
"text": "Megan Foster: Completely. We can start without inbox/calendar and still add value with CRM-stage and the data you already have, then expand. In terms of rep sentiment, we try to position it as *deal protection*. The output is manager-facing too\u2014so instead of \u201cWhy didn\u2019t you log activity?\u201d it becomes \u201cThis deal is slipping because there\u2019s no next meeting scheduled and it\u2019s exceeded typical time in stage.\u201d It\u2019s a coaching artifact.",
"link": "https://www.onyx.app/6774703-13"
},
{
"text": "Fernando Chapman: Okay, but then we\u2019re back to inconsistent stages. If the stage is wrong, you\u2019re diagnosing the wrong thing.",
"link": "https://www.onyx.app/6774703-14"
},
{
"text": "Megan Foster: True. One practical approach we\u2019ve seen: start with a narrow scope\u2014one team, one motion\u2014and use the stage conversion report to *expose* inconsistencies. If Team A has 40% conversion to proposal and Team B has 10%, either they\u2019re doing something different or their stage definitions differ. Sales ops can then standardize. The forecast then becomes a forcing function to clean things up because leadership starts trusting it and asks for it.",
"link": "https://www.onyx.app/6774703-15"
},
{
"text": "Fernando Chapman: That sounds like a lot of change management. We don\u2019t have a big sales ops bench. It\u2019s basically me and one analyst, and we\u2019re already stretched.",
"link": "https://www.onyx.app/6774703-16"
},
{
"text": "Megan Foster: That\u2019s helpful context. If ops bandwidth is limited, we can keep the initial rollout lightweight: one forecast view that leadership cares about, plus a simple \u201cslippage\u201d diagnostic. The goal would be to reduce end-of-quarter surprises rather than do a full process re-architecture on day one.",
"link": "https://www.onyx.app/6774703-17"
},
{
"text": "Fernando Chapman: So what would you actually need from us to get started? Last time you mentioned integrating with Customer_Analyzer\u2014our team liked that fit scoring, but we haven\u2019t rolled it out widely.",
"link": "https://www.onyx.app/6774703-18"
},
{
"text": "Megan Foster: Great memory. Sales_Accelerator can consume Customer_Analyzer\u2019s fit tiers to weight forecast\u2014so high-fit deals get treated differently than low-fit ones even at the same stage. But you don\u2019t have to have it perfect. Minimum to start is: Salesforce access, your current stage definitions, and 12 months of historical opp data if possible. Optional: Customer_Analyzer feed and email/calendar signals later.",
"link": "https://www.onyx.app/6774703-19"
},
{
"text": "Fernando Chapman: The 12 months of historical data is fine. Stage definitions are the messy part. And Salesforce access approvals take time. Also, budget is\u2026 tight. We\u2019re in a hiring freeze.",
"link": "https://www.onyx.app/6774703-20"
},
{
"text": "Megan Foster: Understood on budget. If we can get agreement on scope, I can propose a phased plan: start with forecasting for one pipeline slice and prove improved accuracy and fewer slips. Then you\u2019d have a concrete ROI story internally. Would it be fair to say your biggest concern is that without hygiene, the tool becomes noise and you don\u2019t have time to manage it?",
"link": "https://www.onyx.app/6774703-21"
},
{
"text": "Fernando Chapman: That\u2019s exactly it. We\u2019ve bought tools that promised \u201cinsights\u201d and then we ended up babysitting the data and turning off alerts. I can\u2019t take on another system that needs constant tuning.",
"link": "https://www.onyx.app/6774703-22"
},
{
"text": "Megan Foster: That\u2019s a valid risk. What I\u2019d suggest is a short evaluation where we run Sales_Accelerator against your existing pipeline and show you: (1) forecast accuracy compared to your current method, and (2) the top drivers of slippage. If it\u2019s noisy because of data gaps, we\u2019ll see that quickly\u2014and you can decide not to proceed.",
"link": "https://www.onyx.app/6774703-23"
},
{
"text": "Fernando Chapman: Evaluation is fine, but what does that mean in practice? I don\u2019t want a \u201cpilot\u201d that becomes a sunk-cost project.",
"link": "https://www.onyx.app/6774703-24"
},
{
"text": "Megan Foster: In practice it\u2019s two weeks of data access and a working session. We\u2019ll generate the forecast views\u2014commit/best-case/pipeline\u2014plus the diagnostics by segment/rep/source. Then we review with you: \u201cHere are the deals driving risk, here\u2019s why.\u201d If it\u2019s not credible, we stop. No long implementation.",
"link": "https://www.onyx.app/6774703-25"
},
{
"text": "Fernando Chapman: Two weeks might be optimistic for access and approvals. And even if we get through it, I\u2019m not sure I can get purchasing over the line this quarter. We\u2019re prioritizing donor platform work and compliance projects.",
"link": "https://www.onyx.app/6774703-26"
},
{
"text": "Megan Foster: That\u2019s helpful. Timing-wise, are you saying this is more of a next-quarter conversation even if the evaluation goes well?",
"link": "https://www.onyx.app/6774703-27"
},
{
"text": "Fernando Chapman: Likely, yes. And honestly, even then, I\u2019d need to see a very clear case that it won\u2019t create extra work for ops. If the answer is \u201cclean your CRM first,\u201d that\u2019s a non-starter right now.",
"link": "https://www.onyx.app/6774703-28"
},
{
"text": "Megan Foster: I hear you. I don\u2019t want to oversell it\u2014Sales_Accelerator does benefit from cleaner data. We can reduce manual effort with activity signals, and we can surface where hygiene is hurting outcomes, but it won\u2019t magically fix inconsistent stages overnight. If that foundational work can\u2019t happen, it may not be the right time.",
"link": "https://www.onyx.app/6774703-29"
},
{
"text": "Fernando Chapman: That\u2019s where I\u2019m landing. I like the idea, and I think the forecasting drivers could help, but we\u2019re not in a position to tackle the prerequisites or add another tool this quarter.",
"link": "https://www.onyx.app/6774703-30"
},
{
"text": "Megan Foster: That makes sense. How about we do this: I\u2019ll send a one-page outline of what the evaluation would require\u2014access, timeline, and what we\u2019d deliver\u2014so you can sanity-check effort internally. And if it\u2019s not feasible, we can revisit when your donor platform work settles. Would you prefer I circle back in, say, 90 days?",
"link": "https://www.onyx.app/6774703-31"
},
{
"text": "Fernando Chapman: Yes\u2014send the outline, and let\u2019s plan to reconnect in a few months. If we can improve CRM discipline by then, this might be back on the table.",
"link": "https://www.onyx.app/6774703-32"
},
{
"text": "Megan Foster: Perfect. I\u2019ll send that today. Thanks, Fernando\u2014this was really clear, and it helps me not push you into something that won\u2019t stick.",
"link": "https://www.onyx.app/6774703-33"
},
{
"text": "Fernando Chapman: Appreciate it, Megan. Talk soon.",
"link": "https://www.onyx.app/6774703-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "fernando_chapman@unitypeak_nonprofits.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_5046738",
"semantic_identifier": "5046738 - Ironspruce Robotics - sales_call - Discussion with Nora Wheeler",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-09 00:00:00",
"metadata": {
"meeting_date": "2025-06-09 00:00:00",
"duration_min": "21"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "5046738 - Ironspruce Robotics - sales_call - Discussion with Nora Wheeler",
"organizer_email": "megan_foster@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Megan Foster: Hi Nora, good to see you again. Last time you mentioned you\u2019re trying to be more deliberate about which industries Ironspruce targets next quarter, and that retention plus support burden are big signals for you. Does that still feel like the right focus for today?",
"link": "https://www.onyx.app/6464256-1"
},
{
"text": "Nora Wheeler: Yes, definitely. We\u2019ve got a couple verticals that look promising on pipeline, but we\u2019re not convinced they\u2019re the ones that will stick or be low-friction for support.",
"link": "https://www.onyx.app/6464256-2"
},
{
"text": "Megan Foster: Perfect. Then I want to keep this tight and focus on one part of Customer_Analyzer that maps directly to that\u2014our positioning recommendations for the segments you prioritize. The idea is: once you\u2019ve decided \u201cthese are the industries to lean into,\u201d we generate segment-specific messaging blocks that your team can actually use\u2014website copy, sales deck bullets, email openers\u2014plus an objection-handling playbook tied back to the evidence from your data.",
"link": "https://www.onyx.app/6464256-3"
},
{
"text": "Nora Wheeler: That\u2019s helpful. Our biggest pain is inconsistency. Marketing says one thing, sales says another, and support hears something else entirely.",
"link": "https://www.onyx.app/6464256-4"
},
{
"text": "Megan Foster: Exactly. Let me sanity-check your process today. How are you currently deciding which industries to target\u2014are you looking at renewal rates by vertical, ticket volume, time-to-value, that kind of thing?",
"link": "https://www.onyx.app/6464256-5"
},
{
"text": "Nora Wheeler: It\u2019s messy. Our CRM industry fields aren\u2019t always filled in correctly, and our support tags are inconsistent. We have a general sense from a handful of accounts, but it\u2019s not systematic.",
"link": "https://www.onyx.app/6464256-6"
},
{
"text": "Megan Foster: That\u2019s a very common place to start, and it\u2019s also where Customer_Analyzer tends to help quickly. We ingest the signals you already have\u2014CRM fields, support tickets, product usage\u2014and even if things aren\u2019t perfect, we can still surface patterns. Then the positioning_recommendation feature takes the \u201cfit drivers\u201d we find\u2014like lower support burden or higher retention in a segment\u2014and turns that into messaging and proof points.",
"link": "https://www.onyx.app/6464256-7"
},
{
"text": "Nora Wheeler: I like the idea, but I\u2019m worried about garbage in, garbage out. If our industry field is wrong half the time, does the fit score and the messaging end up wrong too?",
"link": "https://www.onyx.app/6464256-8"
},
{
"text": "Megan Foster: Totally fair concern. Two things there. First, we\u2019re not asking you to magically perfect everything before getting value. We can start with a small set of high-confidence accounts\u2014say the last 30 renewals\u2014and use those as the backbone. Second, the recommendations are linked back to evidence. So if it says \u201cmanufacturing robotics teams care about X,\u201d you can click through and see which tickets, surveys, or notes drove that, and you can edit the block. It\u2019s not a black box that forces you to trust it blindly.",
"link": "https://www.onyx.app/6464256-9"
},
{
"text": "Nora Wheeler: Okay, so it\u2019s more \u201cassisted positioning\u201d than \u201cauto-generated gospel.\u201d",
"link": "https://www.onyx.app/6464256-10"
},
{
"text": "Megan Foster: Exactly. Assisted, fast, and consistent. Here\u2019s how it would look for your use case. Step one: you pick the candidate industries\u2014maybe industrial automation, warehouse logistics, and healthcare robotics\u2014whatever is on the table. Step two: we pull retention and support burden signals for each. Step three: for the top one or two, we generate a messaging set: value proposition, top use cases, differentiators, proof points to collect, and a short objection-handling playbook. That way your SDRs, AEs, and marketing aren\u2019t reinventing language every time.",
"link": "https://www.onyx.app/6464256-11"
},
{
"text": "Nora Wheeler: The objection-handling piece is interesting. Our reps get stuck on the same two objections in certain verticals, and we don\u2019t always have a crisp response.",
"link": "https://www.onyx.app/6464256-12"
},
{
"text": "Megan Foster: Right\u2014so for a given segment, we\u2019ll output something like: \u201cTop objections we see + recommended talk track + which proof points to use.\u201d And it\u2019s grounded in the customer language you\u2019ve collected\u2014support tickets, interview notes, onboarding survey free-text\u2014so it sounds like your customers, not generic marketing.",
"link": "https://www.onyx.app/6464256-13"
},
{
"text": "Nora Wheeler: That\u2019s compelling. Another issue though: our usage data is sparse for some accounts because we don\u2019t have consistent event tracking. We\u2019re mid-way through an instrumentation project.",
"link": "https://www.onyx.app/6464256-14"
},
{
"text": "Megan Foster: That\u2019s also normal. For the first phase, we don\u2019t need perfect product events if we have enough signal from retention, ticket volume, and qualitative notes. We can prioritize \u201cindustries to target next quarter\u201d using those signals, then as your instrumentation improves, the scoring gets richer. The key is: we can start with what you have, and the system will show confidence levels and what data gaps are limiting accuracy.",
"link": "https://www.onyx.app/6464256-15"
},
{
"text": "Nora Wheeler: Confidence levels would help. I don\u2019t want to take a big bet on an industry if the underlying data is thin.",
"link": "https://www.onyx.app/6464256-16"
},
{
"text": "Megan Foster: Totally. So, to make this practical: if we did a pilot, we\u2019d choose two industries you\u2019re already debating. We\u2019d ingest your CRM export plus a slice of support tickets\u2014say the last six months\u2014and any onboarding survey responses you have. Within a week or two, you\u2019d have segment-level fit drivers and then the editable messaging blocks. Your team can review them, tweak language, and deploy it in outbound and on your deck.",
"link": "https://www.onyx.app/6464256-17"
},
{
"text": "Nora Wheeler: What would you need from us to get to that \u201cweek or two\u201d timeline? Because our ops team is stretched.",
"link": "https://www.onyx.app/6464256-18"
},
{
"text": "Megan Foster: Minimal lift. For CRM, a CSV export with account ID, industry field\u2014even if imperfect\u2014ARR, stage history if you have it, and renewal status. For support, a ticket export with tags, category, and free-text subject/body if allowed. If the tags are inconsistent, that\u2019s okay; the text is often the more valuable part for language and objections. If security is a concern, we can start with redacted text or limited fields.",
"link": "https://www.onyx.app/6464256-19"
},
{
"text": "Nora Wheeler: Security is always a concern, but we can likely do a controlled export. Our bigger question is: how do we avoid spending time cleaning data?",
"link": "https://www.onyx.app/6464256-20"
},
{
"text": "Megan Foster: We avoid it by scoping. We don\u2019t boil the ocean. We pick a narrow slice: your top 50 accounts by ARR plus the churned accounts from the last year, mapped to those two industries. That\u2019s usually enough to see meaningful patterns without a months-long data cleanup. And because the positioning recommendations cite evidence, you\u2019ll quickly spot where fields are missing or inconsistent\u2014then you can decide if it\u2019s worth fixing.",
"link": "https://www.onyx.app/6464256-21"
},
{
"text": "Nora Wheeler: That makes sense. I\u2019m also thinking about adoption. If this produces a nice doc but nobody uses it, we\u2019re back where we started.",
"link": "https://www.onyx.app/6464256-22"
},
{
"text": "Megan Foster: Agreed. That\u2019s why the output format matters. It\u2019s not a PDF that dies in a folder. The positioning recommendations come as editable blocks specifically meant to drop into a sales deck, website hero section, and outbound email openers. So we can align with you on where it will live\u2014like \u201cthese bullets go into slide 3,\u201d \u201cthis becomes the SDR opener,\u201d \u201cthis is the objection response in your enablement doc.\u201d",
"link": "https://www.onyx.app/6464256-23"
},
{
"text": "Nora Wheeler: If we can get the sales team to stop freelancing the pitch, that\u2019s already a win.",
"link": "https://www.onyx.app/6464256-24"
},
{
"text": "Megan Foster: Exactly. So here\u2019s what I\u2019d propose as a next step to get you to a buying decision confidently. We run a short pilot focused on your \u201cindustries to target next quarter\u201d decision. Deliverables are: the segment ranking with fit drivers, plus positioning recommendations for the top two industries, including objection handling and proof points to collect. At the end, we do a readout with you where you decide: \u201cDo we trust this enough to use it in Q2 targeting and messaging?\u201d",
"link": "https://www.onyx.app/6464256-25"
},
{
"text": "Nora Wheeler: That\u2019s the right structure. What does pricing look like if we move beyond the pilot?",
"link": "https://www.onyx.app/6464256-26"
},
{
"text": "Megan Foster: For Ironspruce, given your team size and the scope we discussed, you\u2019d be looking at an annual subscription. The pilot fee can be credited toward the annual if you proceed. Rather than guess numbers live, can I confirm two details\u2014how many people would actively use the messaging outputs, and which systems are the data sources you\u2019d want connected longer-term?",
"link": "https://www.onyx.app/6464256-27"
},
{
"text": "Nora Wheeler: Actively, probably marketing plus sales leadership\u2014maybe 8 to 12 people. Systems: HubSpot for CRM, Zendesk for support. Product events later, but not in the pilot.",
"link": "https://www.onyx.app/6464256-28"
},
{
"text": "Megan Foster: Great. That\u2019s enough for me to come back with a clean proposal. If you\u2019re open to it, we can aim for: you send the two exports by next Wednesday, we deliver the first set of positioning blocks the following week, and we do the readout with you and whoever signs off the following Monday. If the outcome is strong, we roll straight into annual without losing momentum.",
"link": "https://www.onyx.app/6464256-29"
},
{
"text": "Nora Wheeler: That timeline works. I\u2019ll need to loop in ops for the exports, but it sounds manageable if we keep it to those top accounts.",
"link": "https://www.onyx.app/6464256-30"
},
{
"text": "Megan Foster: Perfect. I\u2019ll email a one-page checklist for the exports\u2014exact fields, plus an option if you need to limit free-text. And I\u2019ll include an outline of the pilot deliverables so you can share internally.",
"link": "https://www.onyx.app/6464256-31"
},
{
"text": "Nora Wheeler: Do that. If the recommendations really come out as usable sales copy and an objection playbook, I can see this being a no-brainer for us.",
"link": "https://www.onyx.app/6464256-32"
},
{
"text": "Megan Foster: That\u2019s the goal\u2014usable, evidence-linked, and consistent across teams. I\u2019ll send that over in the next hour, and I\u2019ll put a tentative readout on the calendar for two weeks out. Sound good?",
"link": "https://www.onyx.app/6464256-33"
},
{
"text": "Nora Wheeler: Sounds good. Let\u2019s proceed.",
"link": "https://www.onyx.app/6464256-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "megan_foster@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "nora_wheeler@ironspruce_robotics.onyx.app"
}
]
}

View File

@@ -0,0 +1,193 @@
{
"id": "FIREFLIES_5821733",
"semantic_identifier": "5821733 - Ridgeway Lumen Telecom - sales_call - Discussion with Carl Burke",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-16 00:00:00",
"metadata": {
"meeting_date": "2025-06-16 00:00:00",
"duration_min": "23"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "5821733 - Ridgeway Lumen Telecom - sales_call - Discussion with Carl Burke",
"organizer_email": "irene_shen@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Irene Shen: Carl, good to see you again. Thanks for making time for a second call. Last time you mentioned you\u2019re trying to tighten up who you target\u2014especially splitting messaging between SMB and mid-market. Is that still the priority for Ridgeway Lumen Telecom?",
"link": "https://www.onyx.app/7464001-1"
},
{
"text": "Carl Burke: Yeah, it is. We\u2019ve got decent traction in SMB, but mid-market is inconsistent. We\u2019re trying to be clearer on why we win and why we lose.",
"link": "https://www.onyx.app/7464001-2"
},
{
"text": "Irene Shen: Perfect\u2014that\u2019s exactly where Customer_Analyzer tends to help. Today I wanted to focus on one piece: the Positioning Recommendations feature. It takes the signals you already have\u2014CRM fields, onboarding survey responses, product usage, support tickets, call notes\u2014and turns it into segment-specific messaging blocks. So you\u2019d get one set of positioning for SMB and another for mid-market, grounded in what\u2019s actually driving fit and the language customers use.",
"link": "https://www.onyx.app/7464001-3"
},
{
"text": "Carl Burke: Okay. We looked at the deck you sent. I\u2019m still trying to understand how much work this is going to be on our side. And I\u2019m also wary of tools that spit out \u201crecommended messaging\u201d that isn\u2019t really true in the field.",
"link": "https://www.onyx.app/7464001-4"
},
{
"text": "Irene Shen: Totally fair. Let me break it down in practical terms. For your use case\u2014two ICP tiers\u2014we\u2019d set up two segments and then generate: a segment-specific value proposition, top use cases, differentiators, proof points to collect, plus objection-handling. And it\u2019s not just a report\u2014it\u2019s editable blocks you can paste into a landing page hero, outbound email openers, or sales deck bullets.",
"link": "https://www.onyx.app/7464001-5"
},
{
"text": "Carl Burke: That\u2019s the part that makes me nervous, honestly. If it\u2019s auto-generating, it can become a black box. My team\u2019s going to ask, \u201cWhy did it decide that?\u201d And if we can\u2019t explain it, we won\u2019t use it.",
"link": "https://www.onyx.app/7464001-6"
},
{
"text": "Irene Shen: I hear you. The way we address that is by linking each recommendation back to the drivers and evidence. So when it suggests a differentiator for mid-market, it shows what signals drove it\u2014like higher usage of a specific workflow, or repeated language in tickets or calls. The goal is that marketing and sales can look at it and say, \u201cYes, we recognize this,\u201d or \u201cNo, that\u2019s off\u2014let\u2019s adjust.\u201d",
"link": "https://www.onyx.app/7464001-7"
},
{
"text": "Carl Burke: When you say \u201cshows what signals drove it,\u201d what does that actually look like? Is it like a source citation? Or just a generic explanation?",
"link": "https://www.onyx.app/7464001-8"
},
{
"text": "Irene Shen: It\u2019s more like a rationale panel: what inputs were used, what themes it detected, and examples of customer language it\u2019s pulling from. For example, it might surface that mid-market prospects repeatedly mention \u201chandoff between regional teams\u201d and \u201ctime-to-resolution reporting,\u201d and then those become proof points and email angles.",
"link": "https://www.onyx.app/7464001-9"
},
{
"text": "Carl Burke: But we have a lot of messy data. CRM fields aren\u2019t clean. Call notes are inconsistent. Support tickets can be noisy. I worry it\u2019ll make confident recommendations based on garbage inputs.",
"link": "https://www.onyx.app/7464001-10"
},
{
"text": "Irene Shen: That\u2019s a real risk with any system using existing data. What we typically do is start narrow: pick the sources that are most reliable\u2014say CRM closed-won/lost plus a slice of support tickets\u2014and generate an initial draft for SMB and mid-market. Then you review it with your sales leaders and tweak. It\u2019s meant to speed up the iteration, not replace judgment.",
"link": "https://www.onyx.app/7464001-11"
},
{
"text": "Carl Burke: The \u201cspeed up\u201d part is nice. But we\u2019ve been burned before. We tried an analytics tool that promised \u201cICP scoring,\u201d and it basically told us what we already knew, but with a lot of math we couldn\u2019t validate.",
"link": "https://www.onyx.app/7464001-12"
},
{
"text": "Irene Shen: Understood. Customer_Analyzer is similar in spirit but we\u2019ve tried to make the outputs more actionable\u2014especially the positioning blocks. Instead of just saying \u201cmid-market is a 72 fit,\u201d it\u2019s: \u201cHere\u2019s the recommended value prop for mid-market. Here are the use cases that correlate with expansions. Here are the objections you need to preempt.\u201d Then you can put it directly into your landing page and outbound.",
"link": "https://www.onyx.app/7464001-13"
},
{
"text": "Carl Burke: Still\u2014how do I know it\u2019s not just pattern-matching buzzwords? Like, we sell telecom solutions. There\u2019s a lot of generic \u201creliability, uptime, cost savings\u201d language out there. I don\u2019t want another tool telling us to say the same tired stuff.",
"link": "https://www.onyx.app/7464001-14"
},
{
"text": "Irene Shen: That\u2019s exactly why we focus on observed customer language, not generic templates. If your best SMB customers talk about \u201cgetting installs scheduled without back-and-forth\u201d and your mid-market wins talk about \u201cconsolidating vendors across regions,\u201d those are distinct angles. The output should reflect the real phrases and themes coming from your data.",
"link": "https://www.onyx.app/7464001-15"
},
{
"text": "Carl Burke: \u201cShould\u201d is doing a lot of work there.",
"link": "https://www.onyx.app/7464001-16"
},
{
"text": "Irene Shen: Fair. Let me ask a pointed question\u2014what would make you confident? If we could show you, for one segment, the top three differentiators and the exact evidence behind them, would that address the black-box concern?",
"link": "https://www.onyx.app/7464001-17"
},
{
"text": "Carl Burke: It would help. But there\u2019s another issue: our messaging is complicated because we have different product lines. SMB might buy one bundle, mid-market buys another. If the tool creates one narrative, it may not reflect how we actually sell.",
"link": "https://www.onyx.app/7464001-18"
},
{
"text": "Irene Shen: Great nuance. We can model segments by bundle or product line too\u2014but to keep this second call focused, I\u2019d propose we start with the simplest version: SMB vs mid-market for your primary bundle that generated interest last time. Then we can extend it once the team trusts the workflow.",
"link": "https://www.onyx.app/7464001-19"
},
{
"text": "Carl Burke: That sounds like a pilot. We\u2019re not really in a place to start new pilots right now. Budgets are tight, and leadership is asking for immediate pipeline impact.",
"link": "https://www.onyx.app/7464001-20"
},
{
"text": "Irene Shen: Understood. The way teams usually justify it is by tying it to conversion and cycle time. If your outbound and landing pages are dialed for each tier, you should see better meeting rates and fewer \u201cnot a fit\u201d calls. Can I ask\u2014what\u2019s the biggest friction right now in mid-market? Is it top-of-funnel conversion, or deals stalling later?",
"link": "https://www.onyx.app/7464001-21"
},
{
"text": "Carl Burke: Top of funnel. We get meetings, but they\u2019re lower quality than we want. And when we do get serious buyers, they grill us on specifics and we\u2019re not always consistent.",
"link": "https://www.onyx.app/7464001-22"
},
{
"text": "Irene Shen: That maps well to the Positioning Recommendations output\u2014because it includes objection handling and proof points to collect. For mid-market, you\u2019d get a playbook that says: \u201cThey\u2019ll ask about deployment timeline, integration, SLA, reporting.\u201d Then it tells you what proof to gather\u2014case study snippets, metrics, references.",
"link": "https://www.onyx.app/7464001-23"
},
{
"text": "Carl Burke: But again\u2014where does it get that? We don\u2019t have a perfect library of objections categorized. It\u2019s in people\u2019s heads or scattered in Gong and notes.",
"link": "https://www.onyx.app/7464001-24"
},
{
"text": "Irene Shen: Right, and that\u2019s why it ingests those unstructured sources. But I don\u2019t want to oversell. If you don\u2019t have consistent call recordings or notes, the objection-handling may be thinner until more data is in place.",
"link": "https://www.onyx.app/7464001-25"
},
{
"text": "Carl Burke: That\u2019s part of the problem. We\u2019re still standardizing. And I don\u2019t want a tool that requires us to fix our entire data foundation to get value.",
"link": "https://www.onyx.app/7464001-26"
},
{
"text": "Irene Shen: That\u2019s fair. There\u2019s some minimum bar\u2014if the inputs are too sparse or messy, outputs degrade. We can still get value from the sources you trust, but I don\u2019t want you to buy something that you can\u2019t operationalize.",
"link": "https://www.onyx.app/7464001-27"
},
{
"text": "Carl Burke: Exactly. Also, I talked to our marketing lead after your last email. She\u2019s worried the team will treat the generated messaging as \u201capproved\u201d and push it live without enough review. And if it\u2019s wrong, it damages credibility.",
"link": "https://www.onyx.app/7464001-28"
},
{
"text": "Irene Shen: That\u2019s a good governance concern. We position the messaging blocks as drafts\u2014editable and reviewable. But I hear you: how teams use tools matters. We can set permissions and workflows, but it won\u2019t replace internal review.",
"link": "https://www.onyx.app/7464001-29"
},
{
"text": "Carl Burke: So we\u2019re back to: we still need to do the work, plus now we manage another tool.",
"link": "https://www.onyx.app/7464001-30"
},
{
"text": "Irene Shen: You would still do review, yes. The goal is reducing the blank-page time and making the rationale clearer. But if your team isn\u2019t ready to incorporate it into the process\u2014or leadership wants immediate pipeline impact without any iteration time\u2014this might not be the right quarter.",
"link": "https://www.onyx.app/7464001-31"
},
{
"text": "Carl Burke: That\u2019s where I\u2019m landing. I like the idea. I\u2019m not convinced it\u2019ll be accurate enough with our current data, and I don\u2019t have appetite for anything that feels like a black box. If we revisit after we clean up CRM and standardize call notes, it could make more sense.",
"link": "https://www.onyx.app/7464001-32"
},
{
"text": "Irene Shen: I appreciate the directness. Before we close it out\u2014would it be helpful if I sent a concrete example of what the evidence-linked positioning output looks like? Not a generic slide\u2014an annotated sample so you can evaluate whether it feels explainable.",
"link": "https://www.onyx.app/7464001-33"
},
{
"text": "Carl Burke: You can send it, sure. But I want to set expectations: we\u2019re not buying anything this month.",
"link": "https://www.onyx.app/7464001-34"
},
{
"text": "Irene Shen: Understood. I\u2019ll send an annotated sample and a short checklist of \u201cminimum data inputs\u201d so you can assess readiness. If it\u2019s helpful, we can also schedule a short follow-up in, say, six to eight weeks to see if conditions changed.",
"link": "https://www.onyx.app/7464001-35"
},
{
"text": "Carl Burke: That\u2019s fine. Send the sample and the checklist. And we\u2019ll see where we are next quarter.",
"link": "https://www.onyx.app/7464001-36"
},
{
"text": "Irene Shen: Sounds good. Thanks, Carl\u2014this was helpful. I\u2019ll follow up by end of day with those materials and a couple of suggested next steps that don\u2019t require a formal pilot.",
"link": "https://www.onyx.app/7464001-37"
},
{
"text": "Carl Burke: Great. Talk soon.",
"link": "https://www.onyx.app/7464001-38"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "irene_shen@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "carl_burke@ridgeway_lumen_telecom.onyx.app"
}
]
}

View File

@@ -0,0 +1,173 @@
{
"id": "FIREFLIES_6617542",
"semantic_identifier": "6617542 - Jadecircuit Electronics - buy - Discussion with Liliana Zamora",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-18 00:00:00",
"metadata": {
"meeting_date": "2025-06-18 00:00:00",
"duration_min": "34"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "6617542 - Jadecircuit Electronics - buy - Discussion with Liliana Zamora",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hi Liliana, good to see you again. Since this is our third call, my goal today is simple\u2014confirm we\u2019re aligned on what you\u2019re buying, cover the couple of remaining risks you flagged, and if everything looks good, lock in next steps to get Customer_Analyzer live for Jadecircuit. Sound fair?",
"link": "https://www.onyx.app/9164858-1"
},
{
"text": "Liliana Zamora: Yes, that works. We\u2019re interested, I just want to be confident it\u2019ll actually help us decide which industries to target next quarter without turning into a big data cleanup project.",
"link": "https://www.onyx.app/9164858-2"
},
{
"text": "James Choi: Totally. Let\u2019s anchor on your core use case: prioritizing industries using retention signals and support burden. Customer_Analyzer takes the signals you already have\u2014CRM fields, product usage events if available, support tickets, and any onboarding survey data\u2014and produces two outputs you care about: a fit score by segment and practical positioning recommendations for the segments that look best.",
"link": "https://www.onyx.app/9164858-3"
},
{
"text": "Liliana Zamora: The fit score is the part we like. The positioning recommendations sounded promising too, but I don\u2019t want generic \u201cmarketing fluff.\u201d",
"link": "https://www.onyx.app/9164858-4"
},
{
"text": "James Choi: 100%. On the positioning side, the key is it\u2019s evidence-linked. For the top segments, Customer_Analyzer generates editable messaging blocks\u2014like website hero copy, sales deck bullets, and email openers\u2014but every recommendation links back to drivers and the underlying evidence, like \u201chigh retention in this industry\u201d or \u201clower support ticket volume\u201d plus actual language found in tickets or notes. So it\u2019s not fluffy; it\u2019s grounded in what\u2019s working.",
"link": "https://www.onyx.app/9164858-5"
},
{
"text": "Liliana Zamora: Okay. My concern is still data quality. Our CRM industry field is incomplete, and tagging is inconsistent. If the inputs are messy, the scoring might mislead us.",
"link": "https://www.onyx.app/9164858-6"
},
{
"text": "James Choi: That\u2019s the right concern, and we\u2019ve built around it. Two ways we typically handle this without a massive cleanup: First, we can start with the strongest signals you *do* trust\u2014support tickets and retention by account\u2014and treat CRM industry as a \u201cbest effort\u201d field initially. Second, we\u2019ll implement a simple \u201cconfidence\u201d indicator on segments so you can see where scoring is based on solid coverage versus sparse data. Practically, that means you can still prioritize industries next quarter, but you\u2019ll know which recommendations are high confidence versus \u201cneeds more tagging.\u201d",
"link": "https://www.onyx.app/9164858-7"
},
{
"text": "Liliana Zamora: How long does it usually take to get to something usable? We\u2019re planning next quarter\u2019s go-to-market in about four weeks.",
"link": "https://www.onyx.app/9164858-8"
},
{
"text": "James Choi: You\u2019re fine. For most teams, time-to-first-results is about 10\u201314 days once data connections are in place. For Jadecircuit, since you want industry prioritization, we\u2019d connect your CRM and support system first, then map a handful of key fields\u2014industry, ARR band if you have it, renewal status, ticket volume/severity. By week two, you\u2019re already getting a ranked set of segments with fit drivers like \u201cstrong retention + low support burden,\u201d and you can start socializing that internally.",
"link": "https://www.onyx.app/9164858-9"
},
{
"text": "Liliana Zamora: And the positioning recommendations\u2014what exactly would my team get? Like a report?",
"link": "https://www.onyx.app/9164858-10"
},
{
"text": "James Choi: More actionable than a report. For each prioritized segment, you get a positioning package: a segment-specific value proposition, top use cases, differentiators, proof points to collect, pricing/value framing notes, and an objection-handling playbook. And it\u2019s output as editable blocks\u2014so your marketing manager can copy it into a landing page draft, and your sales lead can paste the objection handling into the deck or enablement docs.",
"link": "https://www.onyx.app/9164858-11"
},
{
"text": "Liliana Zamora: That\u2019s attractive, especially the objection handling. Sales keeps running into the same pushback in certain industries and we don\u2019t have a consistent narrative.",
"link": "https://www.onyx.app/9164858-12"
},
{
"text": "James Choi: Exactly. And because it\u2019s tied to drivers, it stays consistent. If the system is telling you \u201cthis segment wins because implementation time is low and support outcomes are strong,\u201d then the recommendations reinforce that. It reduces the internal debate and speeds up creating a narrative that matches reality.",
"link": "https://www.onyx.app/9164858-13"
},
{
"text": "Liliana Zamora: Let\u2019s talk about scope. We don\u2019t want to boil the ocean. If we buy, I want to focus on two or three industries we\u2019re debating right now.",
"link": "https://www.onyx.app/9164858-14"
},
{
"text": "James Choi: Perfect approach. We can start with a \u201csegment bake-off.\u201d In onboarding, we\u2019ll define your candidate industries\u2014say three\u2014and have Customer_Analyzer score them using retention and support burden as primary drivers. Then the positioning recommendations will be generated specifically for the top one or two. That\u2019s a tight, low-risk first phase.",
"link": "https://www.onyx.app/9164858-15"
},
{
"text": "Liliana Zamora: What do you need from us to make that work if the CRM industry field is incomplete?",
"link": "https://www.onyx.app/9164858-16"
},
{
"text": "James Choi: Minimal. We\u2019ll do three things: (1) pull whatever industry data exists today; (2) use support ticket metadata and keywords to help infer industry patterns where possible; and (3) ask you for a small \u201cseed list\u201d of 30\u201350 accounts where you\u2019re confident about industry. That seed set helps calibrate the segment definitions quickly. No giant cleanup project\u2014just enough to make the scoring reliable for the decisions you need.",
"link": "https://www.onyx.app/9164858-17"
},
{
"text": "Liliana Zamora: That sounds manageable. Internally, the only other question is pricing and whether we can justify it with near-term impact.",
"link": "https://www.onyx.app/9164858-18"
},
{
"text": "James Choi: Let\u2019s tie it directly to outcomes. You\u2019re using it to choose which industries to target next quarter and to tighten the narrative for those segments. That\u2019s typically reflected in better conversion and shorter cycles, but even before that, it prevents wasted spend on segments with high churn or high support burden. If Customer_Analyzer helps you avoid just one bad segment bet\u2014or improves win rate in the right segment\u2014that usually covers the cost quickly.",
"link": "https://www.onyx.app/9164858-19"
},
{
"text": "Liliana Zamora: Right. I think we can justify it if we can get something defensible for the QBR and planning deck.",
"link": "https://www.onyx.app/9164858-20"
},
{
"text": "James Choi: You will. The segment fit score output is designed to be shareable: it summarizes the fit drivers\u2014retention, support burden, usage if available\u2014and shows why one industry is prioritized over another. Then the positioning package becomes the \u201cso what do we say?\u201d layer. So you\u2019ll have both: why we\u2019re choosing an industry, and how we\u2019re messaging to it.",
"link": "https://www.onyx.app/9164858-21"
},
{
"text": "Liliana Zamora: Okay. Implementation-wise, do we need IT heavily involved?",
"link": "https://www.onyx.app/9164858-22"
},
{
"text": "James Choi: Light involvement. Usually someone to approve the CRM and support integrations and answer field mapping questions. Most of the work is on our side: mapping, initial scoring, and generating the first set of positioning blocks. Your team\u2019s time is mainly a couple of check-ins to validate: \u201cDo these segments make sense?\u201d and \u201cDoes this language match what you hear?\u201d",
"link": "https://www.onyx.app/9164858-23"
},
{
"text": "Liliana Zamora: Good. If we move forward, I want to start this month so we can use it in planning.",
"link": "https://www.onyx.app/9164858-24"
},
{
"text": "James Choi: Great. Here\u2019s what I propose: we finalize the agreement today, kick off early next week, and aim to deliver the first industry ranking and fit drivers by the end of week two. Then week three we refine and generate the positioning recommendations for the top segment, including objection handling.",
"link": "https://www.onyx.app/9164858-25"
},
{
"text": "Liliana Zamora: What do you need from me to finalize today?",
"link": "https://www.onyx.app/9164858-26"
},
{
"text": "James Choi: Two things: confirmation of the package\u2014Customer_Analyzer with the fit scoring plus positioning recommendations\u2014and your approval to send the order form. Then we\u2019ll schedule the kickoff and send a short data checklist. If you\u2019re comfortable, I can send the paperwork right after this call.",
"link": "https://www.onyx.app/9164858-27"
},
{
"text": "Liliana Zamora: I\u2019m comfortable with that. Send the order form. I\u2019ll route it for signature today, but I\u2019ll need you to include that we\u2019re starting with the three-industry bake-off and the focus on retention and support burden signals.",
"link": "https://www.onyx.app/9164858-28"
},
{
"text": "James Choi: Absolutely. I\u2019ll include that as the initial scope in the statement of work: three candidate industries, scoring based on retention and support burden, plus positioning recommendations for the top one to two segments. I\u2019ll also note the data quality approach\u2014seed list and confidence indicator\u2014so expectations are clear.",
"link": "https://www.onyx.app/9164858-29"
},
{
"text": "Liliana Zamora: Perfect. Then yes, let\u2019s do it.",
"link": "https://www.onyx.app/9164858-30"
},
{
"text": "James Choi: Amazing. I\u2019ll email the order form in the next 15 minutes, and I\u2019ll propose kickoff times for Monday or Tuesday. Once signed, we\u2019ll start integration setup immediately. Anything else you want covered before we hang up?",
"link": "https://www.onyx.app/9164858-31"
},
{
"text": "Liliana Zamora: No, that covers it. Thanks, James.",
"link": "https://www.onyx.app/9164858-32"
},
{
"text": "James Choi: Thank you, Liliana. I\u2019ll get that over right now.",
"link": "https://www.onyx.app/9164858-33"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "liliana_zamora@jadecircuit_electronics.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_7664994",
"semantic_identifier": "7664994 - Ultraviolet Summit Labs - sales_call - Discussion with Natalie Collins",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-03 00:00:00",
"metadata": {
"meeting_date": "2025-06-03 00:00:00",
"duration_min": "16"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "7664994 - Ultraviolet Summit Labs - sales_call - Discussion with Natalie Collins",
"organizer_email": "mina_park@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Mina Park: Hi Natalie\u2014good to see you again. Last time you mentioned you\u2019re trying to get clearer on which customer segments are actually your best fit, especially for product planning. I wanted to use today to show how Customer_Analyzer turns your existing signals into a fit score and then into positioning recommendations you can actually use. Does that still line up with what you\u2019re prioritizing this quarter?",
"link": "https://www.onyx.app/5485394-1"
},
{
"text": "Natalie Collins: Yeah, that\u2019s still the idea. We\u2019re getting pressure to make roadmap bets based on something more solid than anecdotal feedback. But I\u2019m also wary of tooling that looks great in a demo and then falls apart once it hits our actual data.",
"link": "https://www.onyx.app/5485394-2"
},
{
"text": "Mina Park: Totally fair. Why don\u2019t we anchor on one concrete workflow: using Customer_Analyzer to identify high-fit customers with unmet needs so you can prioritize roadmap themes. The two pieces we\u2019d focus on are the fit score\u2014who looks like your best segment right now\u2014and then the positioning recommendation feature, which turns that into segment-specific messaging and objections. Would it help if I walked through what the output looks like and what inputs it needs from you?",
"link": "https://www.onyx.app/5485394-3"
},
{
"text": "Natalie Collins: Sure. But inputs is where I\u2019m concerned. Our CRM fields are\u2026 not clean. Tagging is inconsistent. Usage data is split across tools. If your scoring depends on that, I\u2019m not sure we\u2019ll get anything trustworthy.",
"link": "https://www.onyx.app/5485394-4"
},
{
"text": "Mina Park: That\u2019s one of the most common starting points. Customer_Analyzer can ingest what you have\u2014CRM fields, onboarding surveys if you\u2019ve got them, product usage events, support tickets, even interview notes. The fit score is only as good as the signals, but the system also shows which drivers are being used and what evidence supports them, so you can see \u201cthis score is high because X, Y, Z.\u201d For product planning, we usually start with a smaller set of signals that are more reliable\u2014like plan type, industry, team size, and a couple of usage events\u2014then expand.",
"link": "https://www.onyx.app/5485394-5"
},
{
"text": "Natalie Collins: The transparency part is good. But if half our accounts are missing team size and industry, we\u2019re going to bias the results. Also, our usage event taxonomy changed mid-year, so even that\u2019s messy.",
"link": "https://www.onyx.app/5485394-6"
},
{
"text": "Mina Park: Understood. On the event taxonomy change\u2014Customer_Analyzer can map events into a normalized set, but it does require some upfront definition. On missing CRM fields, you can still run scoring with partial data, but you\u2019re right: it can reduce accuracy. The reason I\u2019m still optimistic is that the positioning recommendation feature can pull language and themes from support tickets and interview notes, which are often richer than CRM fields. So even if scoring is imperfect early, you can still extract \u201cwhat do high-retention customers say they value\u201d and package that into segment messaging.",
"link": "https://www.onyx.app/5485394-7"
},
{
"text": "Natalie Collins: That sounds nice, but we don\u2019t have interview notes in a central place. Support tickets exist, yes, but they\u2019re also inconsistent. And honestly, we don\u2019t want a tool telling us what our messaging is\u2014our marketing team is pretty particular.",
"link": "https://www.onyx.app/5485394-8"
},
{
"text": "Mina Park: For sure\u2014this isn\u2019t meant to replace your team\u2019s judgment. Think of positioning recommendations as a draft that\u2019s anchored in evidence and editable. The outputs come as messaging blocks\u2014website hero copy options, sales deck bullets, email openers\u2014tied back to fit drivers and the customer language it\u2019s drawing from. Marketing can accept, edit, or reject, but it speeds up the iteration cycle and keeps sales and product aligned on what\u2019s resonating.",
"link": "https://www.onyx.app/5485394-9"
},
{
"text": "Natalie Collins: Okay, but how do you avoid it just generating generic copy? We\u2019ve tried \u201cAI positioning\u201d tools and they end up sounding like every other SaaS product: \u201cincrease efficiency,\u201d \u201cstreamline workflows,\u201d all that.",
"link": "https://www.onyx.app/5485394-10"
},
{
"text": "Mina Park: The difference is that our recommendations are constrained by observed inputs\u2014your actual customer signals and the phrases coming from tickets, surveys, notes\u2014plus the fit drivers. So instead of generic \u201cefficiency,\u201d it might surface that a specific segment keeps referencing \u201caudit-ready reporting in two days,\u201d or \u201cfewer handoffs between lab ops and compliance,\u201d and then it builds value props and proof points around that. And it also produces an objection-handling playbook\u2014like \u201cif this segment worries about implementation time, here\u2019s the evidence to collect and the framing that has worked.\u201d",
"link": "https://www.onyx.app/5485394-11"
},
{
"text": "Natalie Collins: I get the concept. I\u2019m just skeptical we can feed it enough consistent data for that to be meaningful. Our support system has a lot of duplicates and weird categorization. And our sales notes are in free text in the CRM\u2014some reps barely write anything.",
"link": "https://www.onyx.app/5485394-12"
},
{
"text": "Mina Park: Makes sense. Can I ask: if you had to pick one near-term win, would it be (a) improving how you prioritize segments for roadmap decisions, or (b) tightening sales and marketing messaging for a segment you already know is attractive?",
"link": "https://www.onyx.app/5485394-13"
},
{
"text": "Natalie Collins: Roadmap. We\u2019re debating two big bets and we don\u2019t agree on which customer type we should optimize for. But again, the data foundation is shaky.",
"link": "https://www.onyx.app/5485394-14"
},
{
"text": "Mina Park: If roadmap is the priority, we can keep scope tight. For example: define 2\u20133 candidate segments you\u2019re already debating, pull whatever consistent signals exist for those accounts, and run a fit analysis just within that cohort. Then the positioning recommendations can summarize the top use cases and differentiators for each segment, and\u2014importantly\u2014highlight what proof points you\u2019d need to collect to validate the bet. It can even call out where evidence is thin.",
"link": "https://www.onyx.app/5485394-15"
},
{
"text": "Natalie Collins: That last part is interesting\u2014calling out where evidence is thin. But I\u2019m still thinking about effort. We don\u2019t have bandwidth for a big data cleanup project just to make a tool work.",
"link": "https://www.onyx.app/5485394-16"
},
{
"text": "Mina Park: Totally. The implementation usually isn\u2019t \u201cclean everything.\u201d It\u2019s more: pick the minimum viable set of fields and signals, map them, and start. Typically a couple CRM fields, one or two product events, and one text source like tickets. If we did a pilot, we\u2019d timebox it to, say, a few weeks and measure whether the outputs are actionable for product planning.",
"link": "https://www.onyx.app/5485394-17"
},
{
"text": "Natalie Collins: A pilot sounds fine in theory, but we\u2019ve been burned. Vendors say \u201cfew weeks\u201d and then it\u2019s a slow integration slog. Plus, security review alone can take us a month.",
"link": "https://www.onyx.app/5485394-18"
},
{
"text": "Mina Park: Understood. If security and integration lead times are a blocker, one option is to start with a lightweight dataset export\u2014CSV from CRM plus a support ticket export\u2014no live integration at first. That can produce an initial fit score and positioning recommendations to prove value before you invest in deeper connections.",
"link": "https://www.onyx.app/5485394-19"
},
{
"text": "Natalie Collins: Exporting could work, but then we\u2019re back to: are those exports even reliable? Our CRM exports miss fields because reps don\u2019t fill them in, and support tickets often don\u2019t map cleanly to customer accounts. I\u2019m worried we\u2019ll spend time and end up with a fancy output that we can\u2019t trust enough to bet the roadmap on.",
"link": "https://www.onyx.app/5485394-20"
},
{
"text": "Mina Park: That\u2019s fair. Let me ask bluntly: what would you need to see to believe the outputs are trustworthy? Is it accuracy of the fit score, alignment with your internal intuition, or some correlation with retention or expansion?",
"link": "https://www.onyx.app/5485394-21"
},
{
"text": "Natalie Collins: Correlation with retention would be the big one. If you can show that the \u201chigh fit\u201d group is actually the group that renews or expands, then we\u2019re talking. But with missing fields and messy tagging, I don\u2019t know how you\u2019d prove that quickly.",
"link": "https://www.onyx.app/5485394-22"
},
{
"text": "Mina Park: We could use renewal status or expansion as an outcome variable, if you have that reliably captured. Even with imperfect feature data, the model can sometimes still find drivers that separate retained vs churned accounts. But yes\u2014if renewal data is also messy, that\u2019s tough.",
"link": "https://www.onyx.app/5485394-23"
},
{
"text": "Natalie Collins: Renewal data is in finance systems, not the CRM. And we don\u2019t have a clean way to join it. So\u2026 that\u2019s kind of the problem.",
"link": "https://www.onyx.app/5485394-24"
},
{
"text": "Mina Park: Got it. Given that, it may be hard to run a pilot that meets your \u201ctrust\u201d bar without some foundational work. We can still generate positioning blocks and an objection playbook from tickets and notes, but if your main decision is roadmap bets, you\u2019ll probably want that retention linkage.",
"link": "https://www.onyx.app/5485394-25"
},
{
"text": "Natalie Collins: Exactly. Marketing might like the messaging blocks, but I\u2019m not buying a platform primarily for that. And product planning is where we need rigor, not just plausible-sounding insights.",
"link": "https://www.onyx.app/5485394-26"
},
{
"text": "Mina Park: That makes sense. If we can\u2019t get to outcome correlation, another path is to use Customer_Analyzer to identify \u201cunmet needs\u201d by clustering themes in tickets and surveys\u2014more qualitative, less scoring-dependent\u2014and then treat the fit score as directional only. But I hear you: that might not be sufficient for executive-level roadmap decisions.",
"link": "https://www.onyx.app/5485394-27"
},
{
"text": "Natalie Collins: Right. Directional is not what leadership is asking for right now. We\u2019re being pushed to justify decisions with numbers, and we\u2019re not set up for that.",
"link": "https://www.onyx.app/5485394-28"
},
{
"text": "Mina Park: I appreciate the clarity. It sounds like the blocker isn\u2019t interest in the capability\u2014it\u2019s the data readiness and the ability to validate against retention. If we were to take a next step, would it be helpful if I sent a short checklist of the minimum data needed to do the retention correlation, so you can assess feasibility with your ops team?",
"link": "https://www.onyx.app/5485394-29"
},
{
"text": "Natalie Collins: You can send it, sure. But I\u2019m being honest: I don\u2019t think we\u2019re going to prioritize stitching finance outcomes to CRM in the next quarter. We have other initiatives underway.",
"link": "https://www.onyx.app/5485394-30"
},
{
"text": "Mina Park: That\u2019s helpful. I don\u2019t want to force a timeline that doesn\u2019t match your reality. How about this: I\u2019ll send the checklist and a sample of what the positioning recommendations and objection playbook look like, so marketing can react. And if later you get to a point where renewal outcomes are accessible, we can revisit a scoped pilot focused on product planning.",
"link": "https://www.onyx.app/5485394-31"
},
{
"text": "Natalie Collins: That works. Send it over. I\u2019m not saying \u201cno forever,\u201d but I can\u2019t move forward to purchase without confidence we can prove the fit scoring is meaningful for us.",
"link": "https://www.onyx.app/5485394-32"
},
{
"text": "Mina Park: Completely understood. I\u2019ll follow up today with the checklist, the sample outputs, and a suggested low-lift pilot plan\u2014clearly labeled as \u201conly if data is available.\u201d Thanks for being candid, Natalie.",
"link": "https://www.onyx.app/5485394-33"
},
{
"text": "Natalie Collins: Thanks, Mina. Talk soon.",
"link": "https://www.onyx.app/5485394-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "mina_park@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "natalie_collins@ultraviolet_summit_labs.onyx.app"
}
]
}

View File

@@ -0,0 +1,169 @@
{
"id": "FIREFLIES_8611294",
"semantic_identifier": "8611294 - Northwind Parcel Services - sales_call - Discussion with Charles Arnold",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-03 00:00:00",
"metadata": {
"meeting_date": "2025-06-03 00:00:00",
"duration_min": "29"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "8611294 - Northwind Parcel Services - sales_call - Discussion with Charles Arnold",
"organizer_email": "james_choi@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "James Choi: Hey Charles, good to see you again. Thanks for making time for a quick follow-up. Last time you mentioned you\u2019re trying to sharpen messaging for two ICP tiers\u2014SMB versus mid-market\u2014so I wanted to focus today on how Customer_Analyzer gets you to a confident segment definition and the actual copy inputs your team can use. Does that still line up with your priority?",
"link": "https://www.onyx.app/6438255-1"
},
{
"text": "Charles Arnold: Yeah, that\u2019s exactly it. We\u2019ve got traction, but our outbound and landing pages feel too generic. We know SMB and mid-market respond differently, but we haven\u2019t nailed what \u201cgood fit\u201d really means.",
"link": "https://www.onyx.app/6438255-2"
},
{
"text": "James Choi: Perfect. The core of what I want to show is our fit estimation\u2014how we produce a fit score from 0 to 100 at both the account level and segment level, plus a breakdown of why. And then we use that to generate positioning recommendations, which translate into value props, differentiators, proof points, and objections\u2014per segment. For today, we can keep it simple and focus on two drivers: time-to-value and adoption/usage patterns. Those are usually the quickest to correlate to \u201cthis segment converts and renews.\u201d",
"link": "https://www.onyx.app/6438255-3"
},
{
"text": "Charles Arnold: Makes sense. My big concern is we\u2019ve struggled just defining segments cleanly. Our \u201cmid-market\u201d sometimes overlaps with certain industries that behave more like SMB. I don\u2019t want another tool that spits out confusing recommendations because our labels are messy.",
"link": "https://www.onyx.app/6438255-4"
},
{
"text": "James Choi: Totally fair\u2014and honestly that\u2019s one of the most common reasons teams stall. What Customer_Analyzer does is make the ambiguity visible and fixable. Instead of relying only on a label like \u201cmid-market,\u201d we look at the drivers behind success. So we might find that, say, a subset of \u201cmid-market\u201d accounts with long implementation cycles and low early usage are actually poor fit\u2014regardless of size\u2014and should be messaged differently or deprioritized.",
"link": "https://www.onyx.app/6438255-5"
},
{
"text": "Charles Arnold: Okay, but how do you avoid us ending up with ten micro-segments and no clarity? We need two tiers for the website and outbound\u2014maybe a few industry pages at most.",
"link": "https://www.onyx.app/6438255-6"
},
{
"text": "James Choi: Great constraint. We can set the output to enforce exactly what you want: two ICP tiers, and then we identify what must be true for each tier. So the system can start with your existing two buckets\u2014SMB and mid-market\u2014and then tell you, \u201cHere\u2019s why these two buckets aren\u2019t clean,\u201d and recommend the minimum changes to make them behave like two distinct groups. Think: tighten the definition by adding one or two criteria, not exploding into ten.",
"link": "https://www.onyx.app/6438255-7"
},
{
"text": "Charles Arnold: Give me a concrete example of what criteria you\u2019d add.",
"link": "https://www.onyx.app/6438255-8"
},
{
"text": "James Choi: Sure. Let\u2019s say we ingest CRM fields and product usage events. If the analysis shows your best-retained customers ramp to a key activation event within, say, 14 days, we can use \u201ctime-to-value within 14 days\u201d as a fit driver. Then your SMB tier might become \u201cSMB + fast time-to-value,\u201d and mid-market might become \u201cmid-market + multi-seat adoption within 30 days.\u201d It\u2019s still two tiers, but now the tiers reflect behaviors tied to outcomes, not just firmographics.",
"link": "https://www.onyx.app/6438255-9"
},
{
"text": "Charles Arnold: That\u2019s interesting. We do have onboarding survey data too, but it\u2019s not clean. Some reps fill it out, some don\u2019t. Would that break the model?",
"link": "https://www.onyx.app/6438255-10"
},
{
"text": "James Choi: It won\u2019t break it. The fit estimation can work with partial signals. We\u2019ll prioritize what\u2019s most consistent\u2014CRM basics and usage events first\u2014then layer in surveys and support tickets as enrichment. And in the driver breakdown, you\u2019ll see confidence and contribution: \u201cThis score is high because adoption is strong,\u201d even if survey completion is low.",
"link": "https://www.onyx.app/6438255-11"
},
{
"text": "Charles Arnold: Alright. So let\u2019s say we get these fit scores. How does that actually translate into landing page messaging? We\u2019re not trying to become data scientists.",
"link": "https://www.onyx.app/6438255-12"
},
{
"text": "James Choi: Exactly. That\u2019s why the second output\u2014positioning recommendations\u2014is framed in marketing language. Once the system sees the top drivers for SMB versus mid-market, it generates segment-specific: (1) value proposition statements, (2) differentiators, (3) proof points you can pull from data\u2014like \u201cteams like you see value in two weeks,\u201d and (4) objection handling\u2014like \u201cimplementation will take too long\u201d for the wrong-fit cohort.",
"link": "https://www.onyx.app/6438255-13"
},
{
"text": "Charles Arnold: The objection handling part sounds useful. We keep hearing \u201csounds nice but we\u2019re too busy to implement.\u201d",
"link": "https://www.onyx.app/6438255-14"
},
{
"text": "James Choi: Right. If the data says mid-market accounts that succeed have a certain adoption pattern\u2014like multiple roles engaging early\u2014we can recommend messaging that sets expectations and reduces that fear: \u201cWe\u2019ll get you to first value in X days with Y steps,\u201d and we can proactively qualify out accounts that historically churn due to low early engagement. That\u2019s directly tied to lowering churn and improving conversion.",
"link": "https://www.onyx.app/6438255-15"
},
{
"text": "Charles Arnold: How quickly can we get to something we can use? We\u2019ve got a campaign refresh in about a month.",
"link": "https://www.onyx.app/6438255-16"
},
{
"text": "James Choi: If you can connect CRM and product usage within the first week, we typically have initial fit scores and a first pass at SMB vs mid-market recommendations in 10\u201314 days. Then week three is refinement: you review the segment definitions, we tighten any overlaps, and you pick the final two-tier ICP definition. By week four you should have segment-specific messaging blocks for landing pages and outbound sequences.",
"link": "https://www.onyx.app/6438255-17"
},
{
"text": "Charles Arnold: You mentioned overlaps\u2014what does refinement look like? I worry we\u2019ll spend time debating definitions internally.",
"link": "https://www.onyx.app/6438255-18"
},
{
"text": "James Choi: We structure it so it\u2019s not a debate. You\u2019ll see side-by-side segment comparisons: SMB vs mid-market with retention, expansion, conversion, support burden, and churn risk signals. When definitions are ambiguous, you\u2019ll see it in the outcomes\u2014like two segments performing the same. Then the question becomes, \u201cWhat\u2019s the smallest change to separate them?\u201d For example, exclude an industry that behaves differently, or add a threshold on adoption. It becomes an evidence-based decision.",
"link": "https://www.onyx.app/6438255-19"
},
{
"text": "Charles Arnold: That would help. Right now it\u2019s basically opinions. One more concern: our industry mix is weird. Parcel and logistics is core, but we have adjacent customers in retail and light manufacturing. Those customers sometimes have totally different needs.",
"link": "https://www.onyx.app/6438255-20"
},
{
"text": "James Choi: That\u2019s a great use case. What we can do is keep the two primary tiers\u2014SMB and mid-market\u2014then flag \u201cindustry as a modifier\u201d only where it materially changes the drivers. If retail mid-market has a different time-to-value and support burden than logistics mid-market, we\u2019ll tell you. But we won\u2019t force you to create a whole new segment unless the data proves it\u2019s necessary for better conversion and retention.",
"link": "https://www.onyx.app/6438255-21"
},
{
"text": "Charles Arnold: Okay. What would you need from us to do a pilot?",
"link": "https://www.onyx.app/6438255-22"
},
{
"text": "James Choi: For a pilot scoped to your landing page + outbound messaging for SMB vs mid-market, we need: access to your CRM fields for account size, industry, lifecycle stage, and outcomes like conversion and renewal; product usage events tied to accounts; and optionally support ticket volume. Then we\u2019ll define your initial segment rules together in a 45-minute working session\u2014keeping it to two tiers\u2014and we\u2019ll run the first fit estimation.",
"link": "https://www.onyx.app/6438255-23"
},
{
"text": "Charles Arnold: And success criteria? I want to make sure we\u2019re aligned before I take this to my VP.",
"link": "https://www.onyx.app/6438255-24"
},
{
"text": "James Choi: Absolutely. For Northwind Parcel Services, I\u2019d propose three pilot success criteria: (1) a validated two-tier ICP definition with clear driver breakdowns\u2014so no more fuzzy SMB vs mid-market debate; (2) segment-specific positioning recommendations that your team uses to update one landing page per tier plus one outbound sequence per tier; and (3) measurable lift signals\u2014early indicators like reply rates or demo-to-close conversion by tier, even if full revenue impact takes longer.",
"link": "https://www.onyx.app/6438255-25"
},
{
"text": "Charles Arnold: That sounds reasonable. Pricing-wise, is this a big annual commitment or can we start smaller?",
"link": "https://www.onyx.app/6438255-26"
},
{
"text": "James Choi: We can start with a scoped pilot and roll into an annual plan once you\u2019ve validated the outputs. Given you\u2019re already motivated to refresh messaging in the next month, the pilot is designed to get you to \u201cusable assets\u201d quickly, not just dashboards.",
"link": "https://www.onyx.app/6438255-27"
},
{
"text": "Charles Arnold: Good. If we do this, I want my marketing lead involved in the refinement session, because she\u2019ll own the landing pages.",
"link": "https://www.onyx.app/6438255-28"
},
{
"text": "James Choi: Perfect\u2014she should be there. Here\u2019s what I suggest: I\u2019ll send a one-page pilot outline today with the data connections needed and the three success criteria we just discussed. Then we schedule the working session early next week with you and your marketing lead. If you\u2019re comfortable after that, we kick off integration immediately.",
"link": "https://www.onyx.app/6438255-29"
},
{
"text": "Charles Arnold: Yeah, send it over. I can get you access to the CRM fields pretty quickly, and I\u2019ll loop in marketing for next week. If we can truly get to two clean tiers and real messaging guidance, that\u2019s worth moving on.",
"link": "https://www.onyx.app/6438255-30"
},
{
"text": "James Choi: Great. I\u2019ll follow up with the outline and two time options for the working session. Thanks, Charles\u2014excited to get Northwind to a sharper ICP and better conversion.",
"link": "https://www.onyx.app/6438255-31"
},
{
"text": "Charles Arnold: Sounds good. Talk soon.",
"link": "https://www.onyx.app/6438255-32"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "james_choi@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "charles_arnold@northwind_parcel_services.onyx.app"
}
]
}

View File

@@ -0,0 +1,177 @@
{
"id": "FIREFLIES_8624132",
"semantic_identifier": "8624132 - Vividcedar Marketing - sales_call - Discussion with Ricardo Alvarez",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-07 00:00:00",
"metadata": {
"meeting_date": "2025-06-07 00:00:00",
"duration_min": "18"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "8624132 - Vividcedar Marketing - sales_call - Discussion with Ricardo Alvarez",
"organizer_email": "mina_park@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Mina Park: Hi Ricardo\u2014good to see you again. Last time you mentioned you\u2019re trying to be more deliberate about which industries Vividcedar targets next quarter, and that\u2019s what sparked interest in Customer_Analyzer. Does that goal still hold, or has anything shifted since we last spoke?",
"link": "https://www.onyx.app/2380708-1"
},
{
"text": "Ricardo Alvarez: Hi Mina. Yeah, still the priority. We\u2019ve got pressure to grow pipeline, but we don\u2019t want to repeat last quarter where support load went up and retention was worse in a couple segments.",
"link": "https://www.onyx.app/2380708-2"
},
{
"text": "Mina Park: That\u2019s exactly the scenario Customer_Analyzer is built for. I\u2019d like to focus today on one core piece: our fit estimation feature. It takes signals you already have\u2014CRM fields, support tickets, usage patterns\u2014and turns them into a fit score from 0 to 100 at both the account and segment level. Then it shows the driver breakdown so you can see why a segment is scoring high or low. If we can map that to your retention and support burden by industry, we can get you to a short list of \u201cgo after these, pause these.\u201d",
"link": "https://www.onyx.app/2380708-3"
},
{
"text": "Ricardo Alvarez: The fit scoring is what caught my attention, but my worry is the data going into it. Our CRM fields aren\u2019t always clean, and usage tagging is inconsistent. I don\u2019t want a fancy score that\u2019s just reflecting bad inputs.",
"link": "https://www.onyx.app/2380708-4"
},
{
"text": "Mina Park: Totally fair, and it\u2019s a common concern. Two things we do to handle that. First, we make missingness visible\u2014so if \u201cindustry\u201d is blank for 30% of accounts, you\u2019ll see how much that\u2019s affecting confidence in the score. Second, you can choose fit drivers that are more reliable for you. For example, if your usage events are sparse, we can lean more on retention outcomes plus support burden signals\u2014ticket volume, escalation rates, time-to-resolution\u2014because those are often cleaner and directly tied to the pain you described.",
"link": "https://www.onyx.app/2380708-5"
},
{
"text": "Ricardo Alvarez: Okay, so it\u2019s not a black box. But we also have weird tagging in support\u2014some teams categorize tickets differently. Would the system misread \u201csupport burden\u201d if ticket types aren\u2019t consistent?",
"link": "https://www.onyx.app/2380708-6"
},
{
"text": "Mina Park: It could if we blindly trusted raw labels. In practice, we usually start with simple, stable measures that don\u2019t depend on perfect categorization\u2014like tickets per active account, reopen rate, median resolution time, and escalation count. If you have consistent \u201cseverity\u201d tagging, great, we can incorporate it. If not, we can still get a strong signal from volume and time-based metrics. Then the driver breakdown makes it obvious if \u201csupport burden\u201d is dominating the score in a way that doesn\u2019t match reality, and we can adjust weighting or swap drivers.",
"link": "https://www.onyx.app/2380708-7"
},
{
"text": "Ricardo Alvarez: That makes sense. How does it help with the \u201cwhich industries next quarter\u201d question, specifically? We already have some retention reports by vertical.",
"link": "https://www.onyx.app/2380708-8"
},
{
"text": "Mina Park: Great point. The difference is it connects the dots across signals and turns it into a decision tool. Here\u2019s how it typically looks: you define segments\u2014say, industry + size band. Customer_Analyzer then compares segments side-by-side: retention rate, expansion, average support burden, time-to-value proxies, and any adoption patterns you have. It outputs a fit score per segment and tells you what drove it. So instead of \u201cHealthcare has 92% retention,\u201d you get \u201cHealthcare mid-market scores 84 because retention is high and support burden is low; Retail SMB scores 41 because support burden is high and early churn spikes in month one.\u201d That\u2019s what lets you prioritize.",
"link": "https://www.onyx.app/2380708-9"
},
{
"text": "Ricardo Alvarez: Got it. We\u2019re debating whether to double down on SaaS startups versus professional services firms. Startups convert fast but churn more; services firms are slower but stable. Would it show that tradeoff?",
"link": "https://www.onyx.app/2380708-10"
},
{
"text": "Mina Park: Yes\u2014and in a way that\u2019s actionable for next quarter. You\u2019d see the fit score split by those segments and a driver breakdown that surfaces the tradeoff explicitly: conversion velocity versus churn risk and support load. Then you can decide, \u201cOkay, startups are a fit if they meet these conditions,\u201d like minimum team size or certain technographic signals, rather than treating all startups the same. It turns a vague debate into an ICP recommendation based on observed outcomes.",
"link": "https://www.onyx.app/2380708-11"
},
{
"text": "Ricardo Alvarez: That\u2019s useful. One more concern: we\u2019re missing \u201cindustry\u201d for a chunk of leads and some accounts. Would that block the segment analysis?",
"link": "https://www.onyx.app/2380708-12"
},
{
"text": "Mina Park: Not block it, but it will limit segmentation until we fill it in. What we usually do is a quick data readiness pass: identify the critical fields for your segmentation\u2014industry and size are the common ones\u2014and quantify completeness. If industry is missing, you can still run scoring at the account level using other drivers, and we can segment on what you do have, like company size or product tier. And we can also flag \u201cunknown industry\u201d as its own bucket so it doesn\u2019t pollute other segments.",
"link": "https://www.onyx.app/2380708-13"
},
{
"text": "Ricardo Alvarez: Okay. If we do that, what do you need from us to get a meaningful first pass? I don\u2019t want a big integration project.",
"link": "https://www.onyx.app/2380708-14"
},
{
"text": "Mina Park: We can keep it lightweight. For a first pass aimed at next-quarter targeting, we typically start with: a CRM export (accounts with industry, size, stage, outcome fields like closed-won/lost if you have them), and a support summary (tickets per account plus resolution times over the last 6\u201312 months). If you have retention/churn by account, that\u2019s ideal. Usage events are nice-to-have; not required to answer your stated use case.",
"link": "https://www.onyx.app/2380708-15"
},
{
"text": "Ricardo Alvarez: And then you produce the segment comparisons and fit scores?",
"link": "https://www.onyx.app/2380708-16"
},
{
"text": "Mina Park: Exactly. Within that, we\u2019d focus on your key question: \u201cWhich industries do we prioritize next quarter given retention and support burden?\u201d You\u2019ll see: fit scores by industry, a ranked list, and the driver breakdown so you can defend the decision internally. We can also set thresholds\u2014like \u201conly pursue segments above 70 fit\u201d\u2014and track how that changes pipeline quality over time.",
"link": "https://www.onyx.app/2380708-17"
},
{
"text": "Ricardo Alvarez: I like the idea of having something we can defend. My leadership team always asks, \u201cWhy this vertical?\u201d and our answer is usually\u2026 a mix of anecdotes and a couple of dashboards.",
"link": "https://www.onyx.app/2380708-18"
},
{
"text": "Mina Park: Right\u2014and this packages the evidence. One thing I want to check: are you aiming to use this mainly for marketing targeting, sales prioritization, or both?",
"link": "https://www.onyx.app/2380708-19"
},
{
"text": "Ricardo Alvarez: Both, but sales is urgent. We need reps to stop chasing segments that eat support capacity later.",
"link": "https://www.onyx.app/2380708-20"
},
{
"text": "Mina Park: Perfect. Then we\u2019ll tune the drivers toward outcomes sales cares about: likelihood to buy and succeed\u2014conversion plus retention and support burden. The fit score becomes a simple shared language: sales can prioritize, marketing can align campaigns, and CS can see what\u2019s coming.",
"link": "https://www.onyx.app/2380708-21"
},
{
"text": "Ricardo Alvarez: What about false confidence early on? If the model\u2019s learning from our past deals, and our past targeting was already biased, are we just reinforcing it?",
"link": "https://www.onyx.app/2380708-22"
},
{
"text": "Mina Park: Good question. Two safeguards: first, the driver breakdown forces transparency\u2014if the score is being driven by something like \u201cwe happened to sell to these industries,\u201d you\u2019ll see it. Second, we encourage testing: pick one \u201cadjacent\u201d segment you\u2019re curious about and run it as a controlled bet next quarter. Customer_Analyzer then measures whether retention/support burden holds up. It\u2019s not just predicting; it helps you iterate on ICP with feedback loops.",
"link": "https://www.onyx.app/2380708-23"
},
{
"text": "Ricardo Alvarez: That\u2019s reassuring. If we move forward, what does onboarding look like timewise?",
"link": "https://www.onyx.app/2380708-24"
},
{
"text": "Mina Park: For the scope we\u2019re discussing\u2014CRM + support summary, segment-level fit estimation\u2014you can usually get an initial readout in about two weeks once data is in. Week one: data pull and normalization, identify missing fields and choose drivers. Week two: scoring, segment comparisons, and an ICP recommendation draft. Then we review together and decide how to operationalize it for reps.",
"link": "https://www.onyx.app/2380708-25"
},
{
"text": "Ricardo Alvarez: Two weeks is reasonable. Pricing aside, I\u2019d want to see a pilot that proves it can actually tell us something we don\u2019t already know.",
"link": "https://www.onyx.app/2380708-26"
},
{
"text": "Mina Park: Completely aligned. Here\u2019s what I propose: a pilot focused on your top 4\u20136 industries plus the two segments you\u2019re debating\u2014SaaS startups and professional services. Success criteria could be: we produce a ranked industry list with clear driver breakdown, identify at least one \u201cdeprioritize\u201d segment with evidence, and define an ICP profile your sales team can use immediately. If we hit that, you\u2019ll have confidence to roll it out.",
"link": "https://www.onyx.app/2380708-27"
},
{
"text": "Ricardo Alvarez: That sounds fair. I\u2019ll need to pull in our RevOps person to help with the CRM export, but I can sponsor it.",
"link": "https://www.onyx.app/2380708-28"
},
{
"text": "Mina Park: Great. To get you to a buying decision, would it help if we outline the pilot in a simple one-pager\u2014scope, data needed, timeline, and what you\u2019ll get at the end\u2014so you can socialize it internally?",
"link": "https://www.onyx.app/2380708-29"
},
{
"text": "Ricardo Alvarez: Yes, send that. Also include what happens if the data is messy\u2014like what minimum completeness you need for industry and churn.",
"link": "https://www.onyx.app/2380708-30"
},
{
"text": "Mina Park: Will do. I\u2019ll include a \u201cdata quality\u201d section with minimum viable fields and what we can do if they\u2019re incomplete\u2014like using size bands or ticket-based burden until industry is cleaned up. Next step: can we schedule a 30-minute working session early next week with you and RevOps to confirm the data pulls?",
"link": "https://www.onyx.app/2380708-31"
},
{
"text": "Ricardo Alvarez: Tuesday morning works. If you send the one-pager today, I can get buy-in before then.",
"link": "https://www.onyx.app/2380708-32"
},
{
"text": "Mina Park: Perfect. I\u2019ll send it this afternoon with a proposed pilot plan and a short checklist for the exports. If Tuesday goes well, we can kick off immediately and aim to have the first segment fit results in two weeks. Sound good?",
"link": "https://www.onyx.app/2380708-33"
},
{
"text": "Ricardo Alvarez: Sounds good, Mina. Let\u2019s do it.",
"link": "https://www.onyx.app/2380708-34"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "mina_park@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "ricardo_alvarez@vividcedar_marketing.onyx.app"
}
]
}

View File

@@ -0,0 +1,165 @@
{
"id": "FIREFLIES_9413408",
"semantic_identifier": "9413408 - Kestrelbloom Agriculture - sales_call - Discussion with Sami Hamdan",
"title": null,
"source": "fireflies",
"doc_updated_at": "2025-06-16 00:00:00",
"metadata": {
"meeting_date": "2025-06-16 00:00:00",
"duration_min": "35"
},
"doc_metadata": {
"hierarchy": {
"source_path": [
"2025-06"
],
"year_month": "2025-06",
"meeting_title": "9413408 - Kestrelbloom Agriculture - sales_call - Discussion with Sami Hamdan",
"organizer_email": "camila_vega@netherite-extraction.onyx.app"
}
},
"sections": [
{
"text": "Camila Vega: Hi Sami\u2014good to see you again. Last time you mentioned you liked the idea of using your Customer_Analyzer fit scores to focus reps and clean up the \u201cspray and pray\u201d follow-up. Today I wanted to show how Sales_Accelerator takes that interest and gets you to consistent execution: tighter forecasting and really practical best-next actions after discovery. Does that still line up with what you\u2019re trying to solve this quarter?",
"link": "https://www.onyx.app/6349311-1"
},
{
"text": "Sami Hamdan: Yeah, it does. The fit scoring is helping, but the follow-through is inconsistent. And forecasting\u2026 we\u2019re still doing a lot of gut feel in the last two weeks of the month.",
"link": "https://www.onyx.app/6349311-2"
},
{
"text": "Camila Vega: Perfect\u2014those are exactly the two areas I want to focus on for this call: (1) sales_forecast, and (2) post-discovery follow-up recommendations for AEs. I\u2019ll keep it concrete and tied to how your team works at Kestrelbloom. Quick check: are you still on Salesforce and logging meetings in your calendar with notes in the CRM?",
"link": "https://www.onyx.app/6349311-3"
},
{
"text": "Sami Hamdan: Yes\u2014Salesforce, and meetings are mostly on Google Calendar. Notes are mixed. Some reps are great, some are\u2026 not.",
"link": "https://www.onyx.app/6349311-4"
},
{
"text": "Camila Vega: That\u2019s common. On forecasting first: Sales_Accelerator sits on top of Salesforce. It takes your stage data, but it doesn\u2019t stop there\u2014it blends in activity signals like last touch, meeting scheduled, email engagement, and stage age. Optionally, it weights forecasts by Customer_Analyzer fit tier, so a Tier 1 ICP deal with clean activity looks different than a Tier 3 \u201crandom inbound\u201d sitting stale in stage 2.",
"link": "https://www.onyx.app/6349311-5"
},
{
"text": "Sami Hamdan: Okay. How is that different from a standard pipeline report plus some custom fields? We\u2019ve tried a few dashboards.",
"link": "https://www.onyx.app/6349311-6"
},
{
"text": "Camila Vega: Great question. The difference is twofold: explainable drivers and diagnostics. You don\u2019t just get a commit number\u2014you get \u201cwhy.\u201d For example, it will show: this deal is in proposal, but stage age is 2x your benchmark, there hasn\u2019t been a meeting scheduled in 14 days, and historical win rate for deals like this\u2014same segment, similar ACV, same fit tier\u2014is 18%, not 55%. Then it flags slippage risk and recommends a concrete next step, like \u201cschedule mutual action plan review with economic buyer\u201d rather than \u201cfollow up.\u201d",
"link": "https://www.onyx.app/6349311-7"
},
{
"text": "Sami Hamdan: The \u201cwhy\u201d part matters. My team will push back hard if it feels like a black box scoring them.",
"link": "https://www.onyx.app/6349311-8"
},
{
"text": "Camila Vega: Totally\u2014and that\u2019s one of the potential pitfalls we see: trust and explainability. We designed the forecast to be \u201cshow your work.\u201d Managers can click into the drivers\u2014stage age, last touch, next meeting, fit tier\u2014and see the benchmarks it\u2019s using. Reps see the same thing, so it\u2019s not a mysterious grade; it\u2019s a set of observable signals. And you can tune which drivers matter most for your business\u2014like if you want \u201cnext meeting scheduled\u201d to be a stronger predictor than email activity, that\u2019s configurable.",
"link": "https://www.onyx.app/6349311-9"
},
{
"text": "Sami Hamdan: That helps. Another concern: reps already complain about too many tools. If this is another screen they have to live in, adoption will be rough.",
"link": "https://www.onyx.app/6349311-10"
},
{
"text": "Camila Vega: Fair. We\u2019re not trying to replace Salesforce\u2014Sales_Accelerator is an execution layer on top. The way most teams roll it out is lightweight: managers use the forecast and risk views in their weekly pipeline meeting, and reps use the \u201cnext action\u201d cards tied to their opps. It\u2019s integrated so they can jump from an opp record, see the drivers, and one-click create the recommended follow-up tasks\u2014without re-entering data. The goal is fewer manual updates, not more.",
"link": "https://www.onyx.app/6349311-11"
},
{
"text": "Sami Hamdan: Okay. Talk to me about the post-discovery follow-up piece. That\u2019s where deals stall for us\u2014people do a discovery call, then the recap is either late or generic.",
"link": "https://www.onyx.app/6349311-12"
},
{
"text": "Camila Vega: Exactly. Here\u2019s the AE use case you described last time, and how it works in Sales_Accelerator: right after discovery, it drafts a recap email based on the meeting notes and CRM fields. Then it suggests two tailored proof points pulled from the approved positioning library\u2014importantly, the same positioning blocks Customer_Analyzer already maintains, so you\u2019re not duplicating messaging. Finally, it creates a mutual action plan: due dates, stakeholders, and next meeting milestone, so you can multi-thread and keep momentum.",
"link": "https://www.onyx.app/6349311-13"
},
{
"text": "Sami Hamdan: When you say \u201cpositioning library,\u201d does that mean we have to build a bunch of assets from scratch?",
"link": "https://www.onyx.app/6349311-14"
},
{
"text": "Camila Vega: Not from scratch. Since you\u2019re already using Customer_Analyzer for fit scores and positioning blocks, Sales_Accelerator consumes that. Practically, you\u2019d start with your top two segments\u2014say, your mid-market growers vs. enterprise co-ops\u2014and map your existing proof points into those blocks. Then the system recommends the right ones based on fit tier and what came up in discovery.",
"link": "https://www.onyx.app/6349311-15"
},
{
"text": "Sami Hamdan: That could be valuable. But again\u2014how does it decide what proof points to use? I don\u2019t want reps sending something that doesn\u2019t match what the customer cares about.",
"link": "https://www.onyx.app/6349311-16"
},
{
"text": "Camila Vega: That\u2019s the other trust angle, and we handle it the same way: transparency. The tool shows, \u201cI\u2019m recommending proof point A because the account is Tier 1 ICP and the discovery notes mention X pain; and proof point B because similar deals won on Y outcome.\u201d And you can require rep approval before anything is sent\u2014nothing has to go out automatically. Over time, you can also lock the library to approved content only.",
"link": "https://www.onyx.app/6349311-17"
},
{
"text": "Sami Hamdan: Good. What about forecasting accuracy\u2014how quickly does it get better? We have some messy historical data.",
"link": "https://www.onyx.app/6349311-18"
},
{
"text": "Camila Vega: You\u2019ll see value quickly even with imperfect history because stage age, last touch, and meeting scheduled are real-time signals. For the historical win-rate benchmarks, we typically start with what you have and improve as you normalize a few fields\u2014segment, source, ACV band, and outcome. Most teams run it in parallel for a few weeks: compare your current commit to Sales_Accelerator\u2019s commit, then calibrate. The important part is it gives you early warning signals before the last two weeks of the month.",
"link": "https://www.onyx.app/6349311-19"
},
{
"text": "Sami Hamdan: That early warning is what I want. Our leadership keeps asking, \u201cWhich deals are real?\u201d and I\u2019m stuck chasing reps.",
"link": "https://www.onyx.app/6349311-20"
},
{
"text": "Camila Vega: Exactly. The forecast view will highlight \u201cat risk\u201d deals with the reasons\u2014like no next meeting, stakeholder missing, or slippage pattern. That gives you a clean coaching moment: \u201cHere\u2019s what needs to happen by Friday to keep this in commit,\u201d and the mutual action plan makes that concrete.",
"link": "https://www.onyx.app/6349311-21"
},
{
"text": "Sami Hamdan: If we did this, what would implementation look like? I don\u2019t want a six-month project.",
"link": "https://www.onyx.app/6349311-22"
},
{
"text": "Camila Vega: It\u2019s not. Since it sits on Salesforce and connects to Customer_Analyzer, the first phase is integration and mapping\u2014usually a couple of weeks. Then we pick one team\u2014maybe your four AEs covering the Midwest region\u2014and run a pilot focused on two motions: post-discovery follow-up and weekly forecast. Success metrics are simple: recap email sent within 2 hours of discovery, mutual action plan created for qualified opps, reduction in stage slippage, and forecast delta vs. actuals.",
"link": "https://www.onyx.app/6349311-23"
},
{
"text": "Sami Hamdan: That\u2019s reasonable. Pricing aside, my last hurdle is internal buy-in. My VP will ask, \u201cHow do we know reps won\u2019t ignore it?\u201d",
"link": "https://www.onyx.app/6349311-24"
},
{
"text": "Camila Vega: The best lever is making it part of existing rituals. For example: in pipeline review, managers pull the Sales_Accelerator forecast and risk drivers\u2014not a rep\u2019s spreadsheet. And for reps, we focus on what saves time: the drafted recap email and one-click mutual action plan. When the tool consistently removes busywork and helps them progress deals, adoption follows. We can also set it up so key actions\u2014like creating the action plan\u2014are the default next step after discovery for qualified opps.",
"link": "https://www.onyx.app/6349311-25"
},
{
"text": "Sami Hamdan: Alright. I\u2019m interested. What do you need from me to move to the next step?",
"link": "https://www.onyx.app/6349311-26"
},
{
"text": "Camila Vega: Great\u2014two things. First, let\u2019s align on the pilot scope: Midwest AEs, focus on post-discovery follow-up plus sales_forecast in weekly reviews. Second, I\u2019d like to book a 45-minute working session next week where you bring your Salesforce admin for the mapping checklist\u2014no big prep, just access and confirmation of fields. If that goes well, I\u2019ll send a short pilot proposal with timelines and success metrics for your VP to approve. Does Tuesday or Wednesday work?",
"link": "https://www.onyx.app/6349311-27"
},
{
"text": "Sami Hamdan: Wednesday morning is best. I can bring our admin. Send me a calendar invite and a one-pager I can forward to my VP.",
"link": "https://www.onyx.app/6349311-28"
},
{
"text": "Camila Vega: Done. I\u2019ll send the invite for Wednesday 10am and a one-pager summarizing the pilot goals\u2014forecast accuracy and faster, higher-quality follow-up\u2014with the explainability piece highlighted so your VP knows it\u2019s not a black box. Anything else you want me to include for her?",
"link": "https://www.onyx.app/6349311-29"
},
{
"text": "Sami Hamdan: Emphasize the \u201cwhy this is at risk\u201d drivers. That\u2019s what leadership keeps asking for.",
"link": "https://www.onyx.app/6349311-30"
},
{
"text": "Camila Vega: Perfect\u2014I\u2019ll make that front and center. Thanks, Sami. I\u2019ll follow up in the next hour with the materials and the invite.",
"link": "https://www.onyx.app/6349311-31"
}
],
"primary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "camila_vega@netherite-extraction.onyx.app"
}
],
"secondary_owners": [
{
"display_name": null,
"first_name": null,
"middle_initial": null,
"last_name": null,
"email": "sami_hamdan@kestrelbloom_agriculture.onyx.app"
}
]
}

Some files were not shown because too many files have changed in this diff Show More