← 一覧へ

dashp

害虫駆除会社向けARR10万ドル($100K)のSaaSを開発
月商 $8,333 · 創業者1人 · 従業員0人 · —
Isaac Z Tai(Founder, dashp)· 創業 October 2022 · 記事掲載 December 17th, 2024 · dashp.io · 元の記事

何の商売か
誰が訪問販売で新規顧客を開拓する米国の害虫駆除会社(零細〜多拠点フランチャイズ)。特に、セールス担当者のコミッション計算やパフォーマンス管理に課題を抱える経営者、セールスマネージャー。
何のために非効率なスプレッドシートやメールでのコミッション計算を自動化し、セールス担当者の「給与不安」を解消したい。技術者の管理、ルート最適化、現場での営業活動、顧客管理、在庫追跡まで一元化して事業運営を効率化し、売上を伸ばしたい。
何を買うか害虫駆除会社向けの業務管理プラットフォーム「DASHP」。セールスチームのコミッション管理、パフォーマンス追跡、コミュニケーション機能を軸に、技術者管理、ルート最適化、フィールドセールス、顧客ポータル、在庫・備品追跡、レポート・分析機能を提供。価格は「スタンダードライセンス」が月額10ドル〜30ドル(約1,500円〜4,500円)、年間契約。柔軟な「フレックスライセンス」はスタンダードの2倍の価格で月額契約(ただし総シート数の30%まで)。

創業者は友人の相談から事業を着想し、最初はノーコードでプロトタイプを開発。その後、モバイルアプリとして実装した。顧客獲得は対面営業(物理的なオフィス訪問)と紹介が最も成功していると記事で述べている。創業1人、従業員0人。月商8,333ドル(約125万円)、年間売上10万ドル(約1,500万円)は2024年12月の記事時点の自己申告。

売値の出典: 記事の本文(2024年12月時点)(2026-10-14 取得)

Who are you and what business did you start?

Hey!! I'm Isaac and I'm a software engineer turned business owner. DASHP, is software for sales teams in the Pest Control industry. Our core customers are typically Pest Control companies that find new customers primarily through door-to-door sales. We help teams manage commission, track performance and communicate. I love this business because it's a peak niche product. Everything I've ever read about building for a specific customer, I've gotten to apply. Most importantly, our users love us and there's no end in what they want us to build! We're doing just over 100k ARR (:

Founder-Market Fit

Skills What skills did Isaac Z Tai have that led to their success? mobile-app built-with-retool built-with-supabase built-with-vercel built-with-expo No Code

How do you come up with the idea for dashp?

A friend of mine who worked in the industry called me asking me whether it was possible to build a tool that helped him calculate commissions for team. Their existing solution was built on spreadsheets and emails. I quickly learned more about this real problem--pay anxiety. These salespeople knocked doors 10+ hours a day, being paid commission-only. Many were students, looking to save for a car, tuition or a wedding. Not knowing their pay in real-time was terrifying. I built a simple demo with no-code tools. The aha moment came when my friend's bosses asked me "what is your company called, and how much does it cost?" At the time, I was full-time at my second struggling AI startup. This was pre-chatGPT, but I was already burning out from the hype. It was refreshing to hear someone tell me that they'd pay for a "boring", simple software solution. A few more meetings with people in the industry confirmed to me that this pain was universal and that the market was hungry for a solution.

How did you build the initial version of dashp?

Here's the exact timeline of that first year in business 2022 October -> Built the no-code prototype, was asked how much it would cost November -> Started writing a mobile app when I kept hearing that's what they wanted December -> Quit my job to go full-time (at zero revenue) January - March -> Fumbling around first attempts at sales and trying to close my first deal. Finished the app. April -> Signed my first contract May -> Signed second customer June - August -> Lot's of bug-fixing and customer support through the first sales season August -> Joined a startup accelerator program I built the initial prototype on Retool. It cost me nothing, since I applied for startup credits and got them. I believe they even have a generous free tier now. When I started to rewrite it into a native app, my only cost was the domain ($12), google workspace ($6 / month) and supabase ($25 / month). I have never exceeded the free tiers of Vercel or Expo Application Services. I did and continue to design, code and sell every single inch of this business myself. It's not a flex, it's just what it took to make this company work. I don't think I would have succeeded if I took on any headcount burn. I didn't raise or borrow any money. (Heading) The Revenue Model: We've gone through lots of different models and are still iterating. But recent contracts have looked something like this: A seat-based contract, with "standard" and "flex" licenses. Standard license -> $10 ~ $30 / month, committed for at least 12 months Flex licenses -> 2x the price of Standard, month-to-month contract Flex licenses cannot make up more than 30% of the total seats This pricing model has come out of a need to balance predictable revenue and the seasonality of users in the industry. Since salespeople are typically active for up to half of the year, it's hard for clients to justify paying for the full year.

How did you launch dashp and get initial traction?

In the beginning I only had one focus: to acquire a single customer and do whatever it took to make them happy. It took me five months from my customers telling me they'd buy my software to actually having a signed contract in place. During this time I learned about how difficult B2B sales actually was and what it took to get these relationships in place. The first year was mostly trial and error. But one thing I did right was develop strong trust with my first customer. This relationship has been crucial in being able to grow the contract and finding more opportunities to solve problems for them. This business involves balancing the needs of three groups of people: the owners of the pest control business, the sales managers and the salespeople. Initially, I thought that I could sell to all three. So tried reaching them at where each group "lived" Business owners -> Visiting physical offices Managers -> Offices + Instagram Salespeople -> Instagram I've learned now that the product needs to be sold the owners directly. Since the software requires a lot of access to private information such as their CRM etc. To this day, the most successful form of customer acquisition is still knocking on physical offices or getting a referral.

What was the growth strategy for dashp and how did you scale?

Since I have a tiny, specific niche, the number of clients that fit my ICP is numbered in the hundreds. This too small an amount to run any effective performance marketing on or to just "dial" from a list on. Instead every potential lead is treated more like a dedicated, long-term business development effort. New customers are shiny and exciting. But for us, a surprising amount of revenue actually comes from upselling existing clients into larger contracts. Once trust is built, we've found that customers shift quickly away from getting the best price to begging us to solve as many problems as possible. Each of these conversations are an opportunity to increase the scope, and therefore the price of the contract. The key to our success has been world-class account management and support. I drop everything to tend to a customer complaint, or request. I'm available at all hours of the day. My personal phone number is proudly plastered all over the app for direct help.

What were the biggest lessons learned from building dashp?

I'm someone that gets easily distracted. I worked for a different company every year in the first four years of my career. And I've always had side hustles. I would attribute 90% of the success so far in simply not giving up and continuing to show up. Efforts compound and so much of reputation and trust is simply based on time. B2B solo businesses are underrated. Often, indiehackers get trapped in building small tools for other creators or build consumer applications. These products tend to accumulate a lot of free or low-quality users that are expensive to support. Churn is also brutal. In B2B, I can focus sales efforts into one or two opportunities and predictably secure significant increases in revenue. Customer support also yields more returns in the long-term because I'm interacting and delighting a smaller, but more impactful group of stakeholders. The total number of users compared to a B2C app is tiny, and that's a good thing. It helps me keep costs low and stay within the free tier of many SaaS offerings.

What platform/tools does dashp use?

Retool Platform 4 using What Isaac has to say about Retool Expo Platform 1 using What Isaac has to say about Expo Google Workspace Productivity 903 using What Isaac has to say about Google Workspace Supabase 11 using What Isaac has to say about Supabase newDocumentReady(function () { $(".database-result-bottom").unbind().click(function() { $(this).find(".database-result-bottom-contents").toggle(); }) // Mobile DB view toggle — switch between the compact row list (default) // and the rich card view, remembered in localStorage. Instant, no reload. // Leading ';' guards against ASI: the preceding statement ends in `})`. ;(function () { var group = document.querySelector('.db-view-toggle-group'); if (!group) return; var rowsPanel = document.getElementById('db-view-rows'); var cardsPanel = document.getElementById('db-view-cards'); function applyView(view) { var isCards = view === 'cards'; if (cardsPanel) cardsPanel.classList.toggle('hidden', !isCards); if (rowsPanel) rowsPanel.classList.toggle('hidden', isCards); group.querySelectorAll('.db-view-toggle').forEach(function (b) { b.classList.toggle('db-view-toggle-active', b.dataset.view === view); }); } var saved = 'rows'; try { saved = window.localStorage.getItem('dbMobileView') || 'rows'; } catch (e) {} applyView(saved === 'cards' ? 'cards' : 'rows'); group.querySelectorAll('.db-view-toggle').forEach(function (b) { b.addEventListener('click', function () { var v = b.dataset.view; try { window.localStorage.setItem('dbMobileView', v); } catch (e) {} applyView(v); }); }); })(); // Search UX: debounced auto-submit + global "/" focus shortcut. // Server-side pg_search is GIN-indexed and returns in <50ms; combined // with the page render, total feedback after the debounce window is // ~500ms — close enough to "live" without an XHR/Turbo refactor. var searchInput = document.getElementById('search'); if (searchInput) { var debounceTimer = null; var lastSubmittedValue = searchInput.value; searchInput.addEventListener('input', function () { clearTimeout(debounceTimer); debounceTimer = setTimeout(function () { // Don't submit if the value hasn't changed (e.g. arrow keys). if (searchInput.value === lastSubmittedValue) return; lastSubmittedValue = searchInput.value; searchInput.form && searchInput.form.submit(); }, 350); }); // Enter still submits immediately — short-circuits the debounce. searchInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); clearTimeout(debounceTimer); searchInput.form && searchInput.form.submit(); } }); } // Global "/" shortcut to focus search — GitHub/Linear pattern. document.addEventListener('keydown', function (e) { if (e.key !== '/' || e.metaKey || e.ctrlKey || e.altKey) return; var t = e.target; if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return; var s = document.getElementById('search'); if (s) { e.preventDefault(); s.focus(); s.select(); } }); function prettySliderValue(func, value) { var n = Math.round(Number(value)); if (func == "money") { if (n >= 1000000000) return "$" + (n / 1000000000).toFixed(n % 1000000000 === 0 ? 0 : 1) + "B"; if (n >= 1000000) return "$" + (n / 1000000).toFixed(n % 1000000 === 0 ? 0 : 1) + "M"; if (n >= 1000) return "$" + (n / 1000).toFixed(n % 1000 === 0 ? 0 : 1) + "K"; return "$" + numberWithCommas(n); } else if (func == "daystomonths") { return (n / 30) + " months"; } else if (func == "hours") { return n + " hours"; } else { return n; } } function getInitialSliderValues(el) { var initial = el.dataset.initial; var defMin = Number(el.dataset.defaultmin); var defMax = Number(el.dataset.defaultmax); if (initial) { var parts = initial.split("|"); var a = parts[0] === "" ? defMin : Number(parts[0]); var b = parts[1] === "" ? defMax : Number(parts[1]); return [a, b]; } return [defMin, defMax]; } function createTwoProngedSlider(id) { var el = document.querySelector(id); if (!el || el.noUiSlider) return; var min = Number(el.dataset.min); var max = Number(el.dataset.max); var step = Number(el.dataset.step); var cleanfunc = el.dataset.cleanfunc; var values = getInitialSliderValues(el); var container = el.closest(".range-filter") || el.parentElement; var input = container.querySelector('input[type="hidden"]'); var label = container.querySelector(".preview-label"); noUiSlider.create(el, { start: values, connect: true, step: step, range: { min: min, max: max }, behaviour: "tap-drag" }); el.noUiSlider.on("update", function(vals) { var lo = Math.round(Number(vals[0])); var hi = Math.round(Number(vals[1])); if (label) label.textContent = prettySliderValue(cleanfunc, lo) + " – " + prettySliderValue(cleanfunc, hi); if (input) input.value = lo + "|" + hi; }); el.noUiSlider.on("change", function() { var form = el.closest("form"); if (form) form.submit(); }); } createTwoProngedSlider("#revenue-slider"); createTwoProngedSlider("#starting-costs-slider"); createTwoProngedSlider("#time-to-revenue-slider"); createTwoProngedSlider("#min-hours-per-week"); createTwoProngedSlider("#market-size"); createTwoProngedSlider("#startup-costs"); createTwoProngedSlider("#case-studies"); $(".mobile-filters-applied-button").click(function() { $(".database-filters").addClass("inView") }) $(".close-mobile-filters").click(function() { $(".database-filters").removeClass("inView") }) // Explore "More filters" — reveal/hide the secondary filter pills. var moreFiltersToggle = document.getElementById("explore-more-filters-toggle"); if (moreFiltersToggle) { moreFiltersToggle.addEventListener("click", function () { var panel = document.querySelector(".explore-secondary-filters"); if (!panel) return; var nowHidden = panel.classList.toggle("hidden"); moreFiltersToggle.setAttribute("aria-expanded", String(!nowHidden)); }); } // Infinite scroll lives in its OWN init block below (initSSInfiniteScroll), // deliberately decoupled from this newDocumentReady callback. Anything that // throws above (a slider lib that didn't load from CDN, a jQuery plugin, // etc.) would otherwise abort the rest of THIS callback and silently kill // infinite scroll — the "only works on a hard reload" bug. Keeping it // separate means nothing here can take it down. }); // ---- Infinite scroll (pro/admin/free-access; table + tile views) ---- // Driven off Kaminari's next-page URL, which the partial renders into // #infinite-scroll-sentinel only when the visitor is eligible. We fetch the // next full page, lift out the children of its .database-append-target // container (<tbody> rows for the table view, tiles for the grid views), // append them, then chain to that page's own sentinel URL. DOMParser does // NOT execute the fetched page's scripts, so only content nodes are added — // sliders/search/PostHog never re-run. The sentinel is absent for non-pro // visitors, so this whole block no-ops for them and their paywall is intact. // // Idempotent: guarded by sentinel.dataset.ssInfiniteInit so re-running on // pageshow (bfcache/back-forward restore, where DOMContentLoaded never fires) // can't double-observe an already-wired sentinel. function initSSInfiniteScroll() { var sentinel = document.getElementById('infinite-scroll-sentinel'); if (!sentinel || !sentinel.dataset.nextUrl) return; if (sentinel.dataset.ssInfiniteInit === '1') return; sentinel.dataset.ssInfiniteInit = '1'; // The "database" view renders TWO append targets — the mobile card list // and the desktop table tbody (one is display:none per viewport). Append // to both so infinite scroll works no matter which is visible. Tile views // have a single target. containers[0] drives the loaded-count readout. var containers = Array.prototype.slice.call(document.querySelectorAll('.database-append-target')); if (!containers.length) { delete sentinel.dataset.ssInfiniteInit; return; } var container = containers[0]; // Infinite scroll is live — hide the prev/next fallback for JS clients. var paginate = document.querySelector('.paginate'); if (paginate) paginate.style.display = 'none'; var numResults = document.querySelector('.num-results'); var loadingEl = sentinel.querySelector('.infinite-scroll-loading'); var nextUrl = sentinel.dataset.nextUrl; var loading = false; // Item nodes only — tile partials (e.g. _video) emit a sibling <style> // per render; those are global CSS we already have, so skip them when // appending and counting. function itemNodes(root) { return Array.prototype.filter.call(root.children, function (n) { return n.tagName !== 'STYLE' && n.tagName !== 'SCRIPT'; }); } function updateCount() { if (!numResults) return; var count = itemNodes(container).length; // Keep the "of N …" suffix; just bump the leading loaded-count. numResults.textContent = numResults.textContent.replace(/Showing\s+[\d,]+/, 'Showing ' + count.toLocaleString()); } function loadMore() { if (loading || !nextUrl) return; loading = true; if (loadingEl) loadingEl.style.display = ''; fetch(nextUrl, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(function (r) { return r.text(); }) .then(function (html) { var doc = new DOMParser().parseFromString(html, 'text/html'); var srcs = doc.querySelectorAll('.database-append-target'); Array.prototype.forEach.call(srcs, function (src, i) { var dest = containers[i]; if (!dest) return; itemNodes(src).forEach(function (node) { dest.appendChild(document.importNode(node, true)); }); }); var ns = doc.getElementById('infinite-scroll-sentinel'); nextUrl = (ns && ns.dataset.nextUrl) || null; sentinel.dataset.nextUrl = nextUrl || ''; updateCount(); if (!nextUrl) { observer.disconnect(); sentinel.style.display = 'none'; } }) .catch(function () { /* leave nextUrl intact so a later scroll retries */ }) .finally(function () { loading = false; if (loadingEl) loadingEl.style.display = 'none'; }); } var observer = new IntersectionObserver(function (entries) { entries.forEach(function (e) { if (e.isIntersecting) loadMore(); }); }, { rootMargin: '600px 0px' }); observer.observe(sentinel); } newDocumentReady(initSSInfiniteScroll); // bfcache/back-forward restore doesn't fire DOMContentLoaded; pageshow does. // Idempotency guard above makes the re-call a no-op when already wired. window.addEventListener('pageshow', initSSInfiniteScroll); .blur-row { filter: blur(4px); pointer-events: none; user-select: none; } /* Mobile view toggle (rows ↔ cards) */ .db-view-toggle { display: inline-flex; align-items: center; gap: 5px; padding: 5px 11px; border: 0; border-radius: 6px; background: transparent; color: #64748b; font-size: 12px; font-weight: 600; cursor: pointer; transition: background-color 0.12s, color 0.12s; } .db-view-toggle-active { background: #0f172a; color: #ffffff; } .database-video-grid { display: grid; grid-template-columns: 1fr; gap: 18px; } @media (min-width: 768px) { .database-video-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } /* Airbnb-style range slider (uses noUiSlider) */ .range-filter { display: inline-flex; flex-direction: column; justify-content: center; gap: 4px; padding: 6px 16px 10px; border: 1px solid rgb(226 232 240); background: #fff; border-radius: 9999px; min-width: 220px; margin-bottom: 0 !important; } .range-filter .range-filter-header { display: flex; align-items: baseline; gap: 6px; line-height: 1; } .range-filter .range-filter-header label { font-size: 0.875rem; color: rgb(100 116 139); font-weight: 500; margin: 0 !important; line-height: 1.2; flex-shrink: 0; } .range-filter .range-filter-header label::after { content: "|"; margin-left: 6px; color: rgb(203 213 225); font-weight: 400; } .range-filter .preview-label { font-size: 0.875rem; color: rgb(15 23 42); font-weight: 600; line-height: 1.2; margin: 0 !important; white-space: nowrap; } .range-filter .filter-slider.noUi-target { height: 3px; background: rgb(226 232 240); border: 0; box-shadow: none; border-radius: 9999px; margin: 3px 6px 0; } .range-filter.is-active { border-color: rgb(15 23 42); background: rgb(241 245 249); } .range-filter.is-active .range-filter-header label { color: rgb(15 23 42); } .range-filter .filter-slider .noUi-connect { background: rgb(15 23 42); } .range-filter .filter-slider .noUi-handle { height: 20px; width: 20px; top: -8px; right: -10px; border-radius: 9999px; background: #fff; border: 1px solid rgb(203 213 225); box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12), 0 2px 4px rgba(15, 23, 42, 0.08); cursor: grab; transition: transform 0.1s ease, box-shadow 0.1s ease, border-color 0.1s ease; outline: none; } .range-filter .filter-slider .noUi-handle::before, .range-filter .filter-slider .noUi-handle::after { display: none; } .range-filter .filter-slider .noUi-handle:hover { border-color: rgb(15 23 42); transform: scale(1.05); } .range-filter .filter-slider .noUi-handle.noUi-active, .range-filter .filter-slider .noUi-handle:focus-visible { cursor: grabbing; box-shadow: 0 0 0 6px rgba(15, 23, 42, 0.08), 0 2px 6px rgba(15, 23, 42, 0.18); border-color: rgb(15 23 42); } .range-filter .filter-slider.noUi-horizontal { height: 4px; } .range-filter input[type="hidden"] { display: none; }

Discover Similar Business Ideas Like dashp

IDEA REVENUE PA Track your peptide protoc... · pepaiapp.com Track your peptide protocol in one simple app. $30K /mo Unblocked Games Site for ... · duckmath.org Unblocked Games Site for kids in school $15K /mo Open-source ai model hub · huggingface.co Open-source AI platform for sharing ML models. — F The only fully Instagram ... · followbuddy.com The only fully Instagram compliant IG unfollower tracking tool. $17K /mo Client onboarding software · binderr.com Client onboarding software for accountants, lawyers, and financial services. $10K /mo GW Ai-powered job search ass... · globalwork.ai Unlock the latest job search strategies with our AI-powered platform $350K /mo Short-form content analytics · viral.app Short-Form Analytics Tracking Platform for TikTok, Instagram &amp; Youtube $5K /mo newDocumentReady(function() { // Save-bookmark hover — delegated on document (guarded once) so rows // appended by infinite scroll get the behavior too. mouseover/mouseout // bubble (mouseenter/leave don't); closest('tr') scopes to the hovered row, // and the relatedTarget check suppresses flicker when moving cell-to-cell // within the same row. if (!window.__saveBookmarkHoverInit) { window.__saveBookmarkHoverInit = true; document.addEventListener('mouseover', function(e) { var row = e.target.closest('tr'); if (!row) return; var saveBookmark = row.querySelector('.save-bookmark'); if (saveBookmark) saveBookmark.classList.remove('invisible'); }); document.addEventListener('mouseout', function(e) { var row = e.target.closest('tr'); if (!row) return; if (e.relatedTarget && row.contains(e.relatedTarget)) return; var saveBookmark = row.querySelector('.save-bookmark'); if (saveBookmark) saveBookmark.classList.add('invisible'); }); } // ---- Tag-pill hover tooltip (LLM-generated context for each Tagging) ---- if (!window.__tagTooltipInit) { window.__tagTooltipInit = true; const tip = document.createElement('div'); tip.className = 'tag-tooltip-popover'; tip.style.cssText = [ 'position:fixed','z-index:9999','max-width:340px','display:none', 'background:#0f172a','color:#fff','font-size:12px','line-height:1.4', 'padding:8px 10px','border-radius:6px','box-shadow:0 8px 24px rgba(0,0,0,.18)', 'pointer-events:none','white-space:normal' ].join(';'); document.body.appendChild(tip); const showTip = function(e) { const target = e.currentTarget; const text = target.getAttribute('data-tag-tooltip'); if (!text) return; tip.textContent = text; tip.style.display = 'block'; const r = target.getBoundingClientRect(); const tr = tip.getBoundingClientRect(); let top = r.bottom + 8; if (top + tr.height > window.innerHeight - 8) top = r.top - 8 - tr.height; let left = r.left; if (left + tr.width > window.innerWidth - 8) left = window.innerWidth - tr.width - 8; if (left < 8) left = 8; tip.style.top = Math.round(top) + 'px'; tip.style.left = Math.round(left) + 'px'; }; const hideTip = function() { tip.style.display = 'none'; }; document.addEventListener('mouseover', function(e) { const el = e.target.closest('[data-tag-tooltip]'); if (el) showTip({ currentTarget: el }); }); document.addEventListener('mouseout', function(e) { const el = e.target.closest('[data-tag-tooltip]'); if (el) hideTip(); }); } });

More about dashp:

Who is the owner of dashp? When did Isaac Z Tai start dashp? How much money has Isaac Z Tai made from dashp? {"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Who is the owner of dashp?","acceptedAnswer":{"@type":"Answer","text":"Isaac Z Tai is the founder of dashp."}},{"@type":"Question","name":"When did Isaac Z Tai start dashp?","acceptedAnswer":{"@type":"Answer","text":"2022"}},{"@type":"Question","name":"How much money has Isaac Z Tai made from dashp?","acceptedAnswer":{"@type":"Answer","text":"Isaac Z Tai started the business in 2022, and currently makes an average of $100K/year."}}]}