月商2万ドル($20K)のチーム向けチャットプラットフォームを開発しました
月商 $20,000 · 創業者1人 · 従業員1人 · TA' XBIEX
Stanislav Dmitruk(Founder, Remote.Team)· 創業 May 2023 · 記事掲載 May 31st, 2025 · remote.team · 元の記事
サービスの初期バージョンは4ヶ月で構築され、最初の30顧客は3ヶ月で獲得した。初の有料顧客は4ヶ月後にポーランドのデジタルエージェンシーから獲得。集客はSEO、LinkedIn/Facebook/Twitter/RedditなどのSNSでの活発な交流、メールマガジン、紹介プログラム、そして『無料リモートワーク監査』を提供することによって行われた。月商2万ドル(約300万円)は2025年5月の記事時点の自己申告。
売値の出典: 公式サイトのトップページ(期間限定の無料期間について記載。有料プランへの言及はあるが価格は未記載)(2026-10-14 取得)
My name is Stanislav, and I manage the development of Remote.Team. We are developing a tool for convenient and efficient management of distributed teams. Our clients range from IT startups and digital studios to large government organizations that value speed, security, transparency and ease of communication. The thing about Remote.Team is that we combine everything you need in a single place: from chat and task management, to surveys and an online consultant widget for a website. We automate all of this routine and help businesses grow faster. Right now, the service is making about $20k monthly. P.S. If your remote work is an eternal chaos - come in, we'll help you put things in order! My image
How did you come up with this business idea? It all started with our own pain of finding a service for collaborative work. But everywhere was the same: tasks get lost, communication is lame, it's not clear who is doing what. And then it hit us: we need 1 convenient tool that will cover all our requirements and pains, and it should not be another slack killer, but a full-fledged digital office that has everything and nothing extra. How does it differ from your other ideas? Remote.Team is a fundamentally different level. Here we are not just automating processes, but have rethought the very principle of remote teams. We are not offering another tool to the current stack, but replacing it entirely. What is your background? I've been managing IT products for 7 years and I know remote teams from the inside out - from development to team management. I myself went from freelancer to service desk, so I understand all the pains firsthand. How was the idea confirmed? At first, we poured MVP and gave it to familiar teammates to poke around. When we started getting positive feedback like “guys, this is exactly what was missing!”, we realized that we were on the right track. Now there are already 100+ teams using our product.
Stanislav built Remote.Team by first identifying the pain points of remote team management and realizing the need for a comprehensive tool. The team started with an MVP to test basic functionality, gathering feedback from familiar teammates. The first version took 4 months to build, and initial traction was gained through a closed beta version and targeted advertising. Challenges included refining the product positioning and focusing on the killer feature, as well as learning from mistakes such as spending too much time on design and not tracking CAC/LTV ratios.
The launch of Remote.Team certainly added gray hairs to our heads, but we started with a closed beta version - offering the product to the CEOs and HR people we knew, who were always complaining about the mess in remote work. After 2 months, we gathered feedback, finalized the platform and started advertising through contextual advertising, profile communities and personal messages in linkedin. As for the reaction of the audience, there is always some skepticism: “Another task manager?”. But when people get a taste and see the level of automation of their processes, their eyes light up. The first 30 customers came in within 3 months. The first payment of $89 after 4 months came from a digital agency in Poland, they were initially working on another platform, but they raised prices on tariffs and decided to look for alternatives, that's how they came across us. The main lessons we learned: - MVP is the base. Don't wait for a perfect product, test if the basic functionality is ready, even if it works crookedly, it will help you to see if there is a future or if you need to change something. Customer feedback is the most important thing in business, because the best ideas are born from real needs.
How we attract clients: - SEO, we primarily focus on search engine optimization of the website so that people looking for tools for remote work find us in search engines. - Social networks Linkedin, FB, twitter, reddit - active interaction with users, answering their questions and gently advertising the service. - Email newsletters - segmented emails help keep in touch with potential customers. - Referral program - this is an additional incentive for users to recommend our tool to friends and colleagues. We award $200 to a referrer for each referred team that met the terms of the program (30% of new clients now go by word of mouth). - Gamification of the workflow. We implemented a rating system for the most active users, allowing teams to see their achievements and receive virtual awards. The result was a 23% increase in team user engagement. Why? It's simple, because all people love recognition and rewards. Real case study on free remote work audit: Problem: companies didn't realize how inefficiently their remote work was organized. Our solution: we offered a free audit of their processes (chats, tasks, control) to identify where up to 13 hours per week were being wasted without any benefit. After the audit, we showed them how we could fix it. Result: 1 out of 6 teams stayed with us. Beginner's tip: Training = trust, explain how your product solves customer problems. Gamification stimulates activity, add motivational elements to the work process if possible. Don't sell functionality - sell results. And in general, the best marketing is solving a customer's real pain. If this can be done, customers will tell about you. For a template of our SEO marketing map, write in a private message - I will give you a free copy.
What proved to be particularly workable and useful? ✅ Focus on the right product positioning and the “killer feature” Initially we wanted to make a “universal tool for collaborative remote work”, but clients didn't understand why they needed another complicated service. Then we emphasized the main advantage - “all tools in one window without switching between 10 tabs”. This became our main argument. ✅ SEO for clients' pain We promoted the queries “how to reduce the number of chats in remote work”, “why tasks in Slack and Trello are lost” instead of “the best tool for remote work”. Traffic from such queries converted 3x better. Where we just got lucky: + During the 2020 pandemic, demand for remote working services increased 5x and we were in the right place at the right time. + The departure of many competitors to Enterprise causing the niche to become more free. Our mistakes: - Spent 3 months on a “perfect” design that doesn't exist. - Didn't keep CAC/LTV records and for a long time the cost of a client was higher than the revenue from him.
Amazon Web Services Platform 147 using Node.js Stack 123 using Angular Stack 44 using Google Analytics Analytics 1,075 using Google Tag Manager Analytics 260 using Figma Design 248 using Figma Design 128 using 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; }
IDEA REVENUE Reddit audience research ... · gummysearch.com Audience research tool for Reddit users $1K /mo Catholic prayer and medit... · hallow.com "Catholic prayer and meditation app fostering faith growth." — Consultant growth platform · mylance.co Platform empowering tech professionals to launch consulting businesses. $18K /mo No-code website builder · dorik.com 'Website builder for non-coders' — Website technology profiler · builtwith.com Website technology profiler for competitive analysis. — Trend discovery platform · treendly.com Trend-spotting platform for untapped market insights. $1K /mo Open-source live support ... · livehelperchat.com Open-source live chat platform for companies. $7.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(); }); } });
Who is the owner of Remote.Team? When did Stanislav Dmitruk start Remote.Team? How much money has Stanislav Dmitruk made from Remote.Team? {"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Who is the owner of Remote.Team?","acceptedAnswer":{"@type":"Answer","text":"Stanislav Dmitruk is the founder of Remote.Team."}},{"@type":"Question","name":"When did Stanislav Dmitruk start Remote.Team?","acceptedAnswer":{"@type":"Answer","text":"2023"}},{"@type":"Question","name":"How much money has Stanislav Dmitruk made from Remote.Team?","acceptedAnswer":{"@type":"Answer","text":"Stanislav Dmitruk started the business in 2023, and currently makes an average of $240K/year."}}]}