our case studies
IDR AI Insights
Today we’re introducing IDR Insight AI, a new tool from Internet Technology Services (ITS) for the federal Independent Dispute Resolution (IDR) process under the No Surprises Act. It helps providers, billing companies and their reps prepare, file and manage out-of-network payment disputes with health plans, faster and with fewer costly mistakes.
IDR Insight AI brings the latest AI technology to the IDR industry, backed by the engineering experience we’ve built since 2009 across more than 100 client projects from our base in Dana Point, California. We’re now preparing a pilot program with a small group of providers, and this post explains what we built, why, and how to get involved.

The current market
When the federal IDR process launched in April 2022, the Departments of Health and Human Services, Labor and the Treasury expected about 22,000 disputes in its first year. By June 2023 they had received nearly 490,000, according to the U.S. Government Accountability Office.
The volume never slowed. As of July 31, 2026, more than 7 million disputes had been initiated since the portal opened, including 2,145,850 in the first seven months of 2026 alone (CMS IDR reports).
Providers win most of the disputes that reach a decision. In the second half of 2025, providers, facilities and air ambulance providers prevailed in about 85% of payment determinations, and the prevailing offer was higher than the qualifying payment amount (QPA) in about 87% of them (CMS).
Where the market is heading
Volume is still climbing. Georgetown’s Center on Health Insurance Reforms (CHIR) reports that disputes rose 77% from 2024 to 2025 to 2.6 million, and that the first six months of 2026 reached 1.75 million, 50% above the same period of 2025 (Georgetown CHIR). CMS counted 394,140 disputes initiated in July 2026 alone, up 24% from June (CMS).
The rules are changing too. A federal IDR operations final rule published June 4, 2026 cut the administrative fee from $115 to $15 per party per dispute for disputes initiated on or after June 11, 2026. The cut is expected to make the process more accessible to smaller providers and facilities, and the Departments anticipate it may increase dispute volume. The same rule formalizes open negotiation notices, adds a hard cap of 50 items per batched dispute with a new “similar condition” batching test for open negotiations starting on or after November 1, 2026, creates a registry for plans and issuers, and requires standardized remittance codes that show whether a claim is subject to the No Surprises Act (Holland & Hart summary).
Costs are drawing scrutiny. The CHIR study estimates the IDR system cost $22.4 billion from 2022 to 2025, including $16.6 billion in 2025 alone, and notes renewed attention from policymakers (Georgetown CHIR; Medical Economics). The takeaway for providers: more disputes, new paperwork requirements and closer scrutiny all reward filings that are accurate, complete and on time.
The demand that isn’t being met
Eligibility is still the biggest stumbling block. Non-initiating parties challenged the eligibility of 42% of disputes in the second half of 2025, and about 19% were found ineligible, most often because of an unfinished 90-day cooling-off period or because the dispute belonged in a state process (CMS). Since April 2022, more than 1.16 million disputes have been closed as ineligible (CMS).
The process is also dominated by a few large players. The top ten initiating parties filed about 66% of disputes in late 2025 (CMS), and CHIR found that many smaller local practices aren’t using IDR at all. One of the study’s authors said navigating it takes resources most small practices lack, including familiarity with the federal portal (Medical Economics).
Labor is the hidden cost. The federal departments estimated plans’ and providers’ internal costs to submit IDR materials at $857 per dispute (Georgetown CHIR), and one software competitor puts manual filing at 25–40 minutes per claim (Sydra). With the administrative fee now just $15, staff time is the main thing keeping smaller providers on the sidelines.

How IDR Insight AI is different
IDR Insight AI was purpose-built for one job: getting federal IDR disputes right. It combines the latest AI technology with rule logic written specifically for the IDR process, and it works from your data rather than generic boilerplate.
- Fast AI judgment calls before you file. IDR Insight AI reviews each dispute for likely eligibility, the right service and code category, and missing evidence. Anything it isn’t confident about is flagged for human review instead of guessed at.
- Probability-based signals, not hunches. See how likely a dispute is to be worth filing, and where an offer sits relative to the QPA compared with how similar disputes have actually been decided.
- Arguments built from your case, not a template. Draft positions are assembled from your own case details and public CMS outcome data, so every packet reflects the actual dispute.
- Hard rules stay hard. Deadlines, batch limits, duplicate checks and out-of-scope services are decided by fixed rule logic, not left to interpretation.

The pricing model is different, too. Much of the outsourced IDR help on the market charges a contingency: a percentage of whatever extra you recover. Published rates range from 10% of the improvement over the payer’s initial offer (Claims Assassins) to “20 percent or more” as described by one software competitor (Sydra). In a services agreement filed with the SEC, HaloMD, one of the largest IDR filers, charged a client 20% of the net recovery on federal IDR awards, while the client still paid the IDR entity and CMS administrative fees up front (SEC filing). A June 2026 amendment lowered that to 16% for awards issued on or after July 1, 2026 (amendment).
Using the worked example in that agreement (written for a state arbitration claim, but the math is the same), a $2,000 award less a $100 initial payment, $100 patient responsibility and a $115 administrative fee leaves a $1,685 net recovery. At 20%, the firm’s fee is $337. At 16%, it’s $269.60 (SEC filing). Across 100 similar wins, that’s $26,960 to $33,700, and it grows with every dollar you win. Government fees, by contrast, are modest: the $15 administrative fee per party, and a certified IDR entity fee of $200–$840 for a single determination that the losing party pays (CMS; CMS entity fee list).
IDR Insight AI is built to be priced as software, not as a cut of your recovery, so the upside of a win stays with the provider.

Feature highlights
- AI-drafted, paste-ready filings and evidence packets organized for the CMS IDR portal (CMS offers no public filing API, so you file in your own account and stay in control).
- Automatic deadline calculation in federal business days from your anchor date for open negotiation, IDR initiation, entity selection and offer submission.
- Built-in CMS and NUBC code sets: place of service, HCPCS codes and modifiers, bill types and revenue codes, plus CPT format checks.
- Date and price validation that catches problems before they become ineligibility findings.
- Forms that keep your work when a validation error appears.
- Batching and bulk import from CSV or Excel, with batch suggestions that follow federal rules.
- Case tracking for every open dispute, its status and its next deadline.
- Outcome insights from CMS public use files: historical win rates and typical prevailing offers relative to the QPA by service code, state and certified IDR entity.
Join the IDR Insight AI pilot
We’re looking for a few pilot partners to help us get IDR Insight AI up and running in real-world workflows. It’s a good fit if you’re an independent or specialty practice, a medical billing company or a revenue cycle management firm handling out-of-network claims.
What pilot partners get:
- Early access to IDR Insight AI before general release
- Hands-on onboarding and setup by our team
- Direct input on the product roadmap
- Founding-partner pricing, set together with us
What we ask in return:
- Real out-of-network dispute volume to run through the tool
- Honest, regular feedback on what works and what doesn’t
Ready to talk? Contact Nick Nguyen at ITS: call (949) 446-1716 or email its@InternetTechnologyServices.com. You can also request access at idr.internettechnologyservices.com.
Note: IDR Insight AI is a software tool, not legal advice. Consult qualified counsel about your specific disputes.
Sources: GAO-24-106335; CMS, Independent Dispute Resolution Reports; CMS, Supplemental Background on the Federal IDR PUF, July 1 – December 31, 2025; Georgetown CHIR, Aug. 2026; Medical Economics, Aug. 27, 2026; Holland & Hart, June 26, 2026; CMS IDR fee final rule fact sheet; CMS list of certified IDR entities; HaloMD–Nutex agreement (SEC); First Amendment to HaloMD–Nutex agreement; Claims Assassins; Sydra.
A free tool for people who text themselves out of getting laid.
You know that moment.
They’re warm. The vibe is there. You might have even got the half naked selfie, your pretty much in..
Then your thumbs clock in overtime.
“hey just checking in lol”
“wait did I say too much”
“ok last one I swear”
“haha ignore that”
Congrats. You didn’t get ghosted by them. You got ghosted by you.
What Ive built is an AI dating coach integrated into your text messaging/SMS to help keep the vibe flowing

What it does
Sits between you and your SMS dating life (using the popular and free phone service GV (G00G13 V01C3) , because US SMS is a compliance maze and this is the cheap workaround)
Helps you draft replies that sound like a person who has options, not a person refreshing their phone every 90 seconds
Comes with PUA-leaning defaults if you want that energy
Lets you teach the AI who you are (age, work, interests) system-wide


You can sculpt how it handles each person your talking to as well.
Remembers each conversation (and you can edit that memory when it gets weird)
Gives you drafts so you can send something that doesn’t smell like panic
What it is not
It is not real time carrier SMS. It is a hack.
Scraping / syncing / sending takes longer.
If you’re technical, you can get it running on a cheap Linux VPS and an LLM account for damn near free
The hardest part is remote login to GV (VPS + VNC). After that, it’s a breeze.
If GV updates there web ui selectors die, hopefully the community patches them. Open source exists for a reason.
Who this is for
You write novels when “sounds good” would have worked
You have lost nights you were winning because your follow-up needed a follow-up
You want coaching energy without paying $300/hr for someone to say “wait longer”
Who this is not for
Anyone looking for a polished SaaS with a ToS longer than your last situation ship
Anyone who needs carrier-grade instant delivery
Anyone who thinks AI should auto-send their entire personality unsupervised (you still hit send)
This does require a server and technical skills to get up and running detailed instructions are in the repo. Those that arent technically inclined and have the discretionary funds can hire me to install it for them.
SMS wingman is free and opensource: Download SMS Wingman
If it helps you get a date: tip, donate, or hire us
Featured Project: Belvedere AI -Your Phone, Answered — Even When You Can’t
Most small businesses don’t lose leads because their product is bad. They lose them because the phone rang once, hit voicemail, and the caller moved on.
I’ve seen that pattern for years building sites and systems for local shops. The front door of the business isn’t always the website — it’s whoever answers (or doesn’t) when a real customer calls.
That’s why I built Belvedere AI.
Belvedere is an AI phone butler. It answers in a natural voice, handles the conversation, and leaves you a recording and transcript so you’re never starting from zero. It works with the number you already have. No new phone line to remember. No hardware. No “press 1 for sales” maze.
The problem Belvedere solves
For business owners: You’re on a job, with a client, or after hours. The phone rings. Voicemail. That lead often never calls back — especially if they’re comparing two shops and the other one answered.
For individuals: You don’t want every unknown number. Scammers are getting better at sounding human — including AI-cloned voices that can mimic someone you know. Gut instinct alone isn’t the filter it used to be.
Belvedere sits in front of your calls and handles both: capture the real ones, screen the sketchy ones, and brief you before anything connects when that’s what you want.
What every Belvedere does
- Sounds human — real conversation, not a menu tree
- Keep your number — call forwarding; nothing to port or install
- Recordings & transcripts — review when you have a minute
- On when you want it — flip on/off from your phone in seconds
- Preview before you go live — hear it handle a call first
- Live in minutes — pick a voice → forward → go live
Personal vs business
Personal is for individuals: caller-specific handling, casual tone, control over who gets through.
Business answers as your company — professional receptionist tone, tuned to capture who called, what they need, and how to follow up, including around the clock. It can answer common service questions, help with availability/booking, and take a clean message when it shouldn’t connect yet.
Scam & unknown-caller screening
Every caller explains who they are and why they’re calling. Familiar voices and Caller ID aren’t trusted alone. Urgency (“IRS,” tech support, family emergency) doesn’t get a free pass. Belvedere never asks for passwords, PINs, OTPs, SSNs, or card numbers — and treats callers who push for those as a red flag. After screening it can privately brief you; the call connects only if you say yes. When something’s off, it takes a message and ends the call safely.
Setup in three steps
- Pick a voice (clone yours or use a ready-made one)
- Forward your number
- Go live
Flat pricing with included minutes; more minutes in one tap if you need them. Cancel anytime.
Try it
Belvedere: https://belvedere.internettechnologyservices.com Screening: https://belvedere.internettechnologyservices.com/security
Case Study: GuideToRide.com Vibe Coded
I code a robust marketplace with AI
As a passionate mountain biker who loves exploring new trails, I found it challenging, and initially most of my time was spent getting lost. Along the way, I realized having a local show me around makes a huge difference in the fun factor of the ride. So I created guidetoride.com to address a common pain point for riders, with the solution being to connect riders with locals, removing the friction for exploring riders associated with exploring new trails, meanwhile supporting local community riders.
I gave up along the way, the technology wasn’t there yet. Anthropics’ Claude was the game changer; working with AI felt like I was collaborating with a senior-level developer. The most remarkable aspect was that AI didn’t require small talk and was available to work 24/7, which made the development process incredibly enjoyable and efficient. Initially, I encountered some minor mistakes—like variable names being incorrect or functions not quite aligning with my intentions. However, as the technology evolved, so did my AI partner’s capabilities.
Over time, it began making thoughtful recommendations that enhanced the coding process. While Claude was busy scripting, I seized the opportunity to immerse myself in learning about new tech stacks and APIs. This knowledge allowed me to effectively direct my AI partner in the implementation of various features. I found myself not just coding, but educating myself on how to optimize the development process.
In my quest for efficiency, I automated my workflow significantly. Implementing Docker streamlined my application environments, and I adopted a meticulous Git workflow to manage my code versions effectively. Leveraging AWS services like ECR and App Runner enabled me to achieve one-click deployments, significantly cutting down the time to launch updates.
Additionally, I automated payment distribution through Stripe Connect accounts, which simplified the financial transactions related to the project. I also integrated Amazon S3 to serve the front end, ensuring fast and reliable access to static resources. To manage the connection between the backend and frontend efficiently, I utilized CloudFront, which provided low-latency delivery of my content.
This comprehensive automation of my development workflow has allowed me to focus on innovation rather than operational concerns. I no longer have to worry about scaling servers in production; the infrastructure I’ve built is robust and adaptable. The combination of an intelligent AI partner and a well-structured, automated system has transformed my approach to development, ultimately making the process smoother and significantly more productive.











Case Study: Building Your Own Multi-Agent AI Sales Platform with Real-Time Human Support
Have you ever wanted to create a sophisticated AI sales system that combines multiple specialized AI agents while allowing human team members to monitor and step in when needed? In this DIY guide, I’ll walk you through how we built our own system using OpenAI’s Assistants API, WebSockets, PHP, and React.
What You’ll Need
- A web server with PHP support (we used WAMP)
- MySQL database
- OpenAI API key
- Basic knowledge of PHP, JavaScript, and React
- Familiarity with WebSockets
Step 1: Set Up Your Project Structure
Start by creating your project directory structure:
CopyInsert/your-project-name/
├── admin/
│ ├── index.php
│ └── login.php
├── vendor/
├── config.php
├── index.html
├── style.css
├── websocket_server.php
└── composer.json
Step 2: Configure Your Environment
Install required dependencies using Composer:
{ "require": { "cboden/ratchet": "^0.4.4", "openai/client": "^1.0.0", "phpmailer/phpmailer": "^6.8" } }
Create a config.php file with your database and API credentials:
<?php define(‘DB_HOST’, ‘localhost’); define(‘DB_USER’, ‘your_username’); define(‘DB_PASS’, ‘your_password’); define(‘DB_NAME’, ‘your_database’); define(‘OPENAI_API_KEY’, ‘your_openai_api_key’); // Define your OpenAI assistant IDs define(‘AISALESMANAGER’, ‘your_manager_assistant_id’); define(‘NEWSALESAGENT’, ‘your_new_sales_assistant_id’); define(‘EXISTINGSALESAGENT’, ‘your_existing_sales_assistant_id’); define(‘ESTIMATESAGENT’, ‘your_estimates_assistant_id’); // For email notifications define(‘SMTPUSER’, ‘your_smtp_username’); define(‘SMTPPASS’, ‘your_smtp_password’); ?>
Step 3: Set Up Your Database
Create your MySQL database with the necessary tables:
CREATE DATABASE your_database;
USE your_database;
CREATE TABLE conversation_summaries (
id INT AUTO_INCREMENT PRIMARY KEY,
thread_id VARCHAR(255) NOT NULL,
user_ip VARCHAR(45) NOT NULL,
summary TEXT,
current_agent VARCHAR(255),
status ENUM('active', 'completed') DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
thread_id VARCHAR(255) NOT NULL,
message_id VARCHAR(255) NOT NULL,
role ENUM('user', 'assistant', 'system', 'admin') NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX (thread_id)
);
Step 4: Create Your WebSocket Server
The heart of our system is the WebSocket server that handles real-time communication. Here’s how to build it:
Create websocket_server.php:
<?php require 'vendor/autoload.php'; require_once 'config.php'; use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; use React\Socket\Server as SocketServer; use React\EventLoop\Factory; use Ratchet\ConnectionInterface; use Ratchet\MessageComponentInterface; class ConversationServer implements MessageComponentInterface { protected $clients; protected $clientThreadMap = []; // Map clients to thread IDs protected $userClients = []; // Track user clients private $conn; private $openAIClient; public function __construct() { $this->clients = new \SplObjectStorage; $this->userClients = new \SplObjectStorage; // Initialize database connection $this->conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); // Initialize OpenAI client $this->openAIClient = \OpenAI::client(OPENAI_API_KEY); } // Implement the required methods: onOpen, onMessage, onClose, onError // [Add implementation details here] } // Create and start the server $loop = Factory::create(); $socket = new SocketServer('0.0.0.0:8080', $loop); $server = new IoServer( new HttpServer( new WsServer( new ConversationServer() ) ), $socket, $loop ); echo "WebSocket server running at 0.0.0.0:8080\n"; $loop->run();
Step 5: Build Your User Interface
Create a user-friendly chat interface using React:
- In your
index.html, set up the basic structure:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AI Sales Assistant</title> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="style.css"> </head> <body> <div id="app"></div> <script src="https://unpkg.com/react@17/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/babel"> // Your React application code goes here </script> </body> </html> - Create your React chat component with WebSocket connection:
- function App() { const [messages, setMessages] = React.useState([]); const [threadId, setThreadId] = React.useState(null); const [inputValue, setInputValue] = React.useState(”); const [isProcessing, setIsProcessing] = React.useState(false); const socket = React.useRef(null); // Initialize WebSocket connection React.useEffect(() => { const wsProtocol = window.location.protocol === ‘https:’ ? ‘wss:’ : ‘ws:’; const wsHost = ‘localhost:8080’; const wsUrl = `${wsProtocol}//${wsHost}`; const newSocket = new WebSocket(wsUrl); // Handle WebSocket events (onopen, onmessage, onclose, onerror) // [Add implementation details here] socket.current = newSocket; return () => { if (newSocket) { newSocket.close(); } }; }, []); // Implement message handling and UI components // [Add implementation details here] return ( // Your chat UI JSX goes here ); } ReactDOM.render(<App />, document.getElementById(‘app’));
Step 6: Create the Admin Interface
Build an admin panel to monitor and manage conversations:
- Create
admin/index.php:phpCopyInsert<?php session_start(); require_once '../config.php'; // Implement authentication if (!isset($_SESSION['admin_authenticated'])) { // Show login form or authenticate // [Add implementation details] } // Fetch active conversations from database $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); $query = "SELECT * FROM conversation_summaries ORDER BY created_at DESC"; $result = $conn->query($query); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Admin Panel</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body> <!-- Implement your admin UI here --> <!-- Left panel for conversation list --> <!-- Right panel for viewing and participating in conversations --> <script> // JavaScript for WebSocket connection and admin functions // [Add implementation details] </script> </body> </html>
Step 7: Configure Your OpenAI Assistants
You’ll need to create four different assistants in the OpenAI platform:
- AI Sales Manager: Configure with instructions to identify user needs and direct to the appropriate specialized agent
- New Sales Agent: Specialize in handling new customer inquiries
- Existing Sales Agent: Optimize for returning customer interactions
- Estimates Agent: Focus on gathering requirements and generating estimates
For each assistant, use the OpenAI web interface to:
- Create a new assistant with appropriate name and description
- Set up the model (we recommend GPT-4)
- Add detailed instructions for the assistant’s role
- Enable structured output for agent handoffs
Step 8: Implementing Agent Handoffs
The key to our multi-agent system is the handoff mechanism. In your WebSocket server, implement handling for structured outputs:
private function processAssistantResponse($threadId, $runId) {
// Retrieve the run and check for structured output
$run = $this->openAIClient->threads()->runs()->retrieve(
threadId: $threadId,
runId: $runId
);
// Check for structured outputs indicating agent handoffs
if (property_exists($run, 'structured_outputs') && !empty($run->structured_outputs)) {
foreach ($run->structured_outputs as $output) {
if (property_exists($output, 'next_agent')) {
$nextAgent = $output->next_agent;
// Update the agent in the database
$this->updateConversationAgent($threadId, $nextAgent);
// Create a new run with the next agent
$this->openAIClient->threads()->runs()->create(
threadId: $threadId,
parameters: [
'assistant_id' => $nextAgent
]
);
return true; // Handoff initiated
}
}
}
return false; // No handoff
}
Step 9: Start Your WebSocket Server
Create a simple script to start your WebSocket server:
# start_websocket_server.bat
php websocket_server.php
Step 10: Testing Your System
- Start your WebSocket server
- Open your main site in one browser window
- Open the admin panel in another browser window
- Test the entire flow:
- Start a conversation as a user
- Observe the conversation in the admin panel
- Test agent handoffs by triggering different scenarios
- Test human takeover from the admin panel
Troubleshooting Common Issues
- WebSocket Connection Failures: Ensure your server allows WebSocket connections and that ports are properly configured
- OpenAI API Errors: Verify your API key and check OpenAI’s status page for service issues
- Database Connection Problems: Confirm your database credentials and that your tables are properly set up
- Agent Handoff Issues: Review your structured output format and ensure your assistants are properly configured
Listen to our Podcast

To learn more about us
We used AI to create a podcast introducing you to our company. Enjoy!
Case Study: The Latest Mobile App we built
Navenu iOS and Android Apps
Leveraging the latest in programming technology with React Native, we successfully developed robust iOS and Android mobile applications for Navenu from a single codebase. This approach resulted in significant savings in both development time and cost.
Nick Nguyen, who heads projects at Internet Technology Services, was appointed as CTO at Navenu, where he spearheaded the comprehensive development of the application.
The powerful Navenu app launched successfully, featuring a curated network of over 500 venues across London and Dubai, alongside 100 curators/influencers. Key features include a personalized AI Concierge, unique “Venue Drops” (likely special offers or content), push notification capabilities, and a sophisticated publishing platform.
Navenu Showcase
Case Study: The case against shared hosting
Case Study: Using Web Bots to hunt for B2B marketing leads
How We Obtained Vendor Email Addresses
Let’s use Houzz.com as our target for this example (for educational purposes). Our goal was to obtain email addresses from the businesses listed in their online vendor directory.
The core problem was that the email addresses ARE NOT directly available on the Houzz website.
Below, we’ll walk you through the strategy and implementation we used to overcome this challenge and acquire the necessary data.

The Houzz BOT at work….Console reporting back results
Analysis of Site and Strategy Used
Houzz vendor listings
Targets Indexed
So, we used the Houzz vendor listing pages to index all the vendors, who were our initial targets.

Vendor Detail Page
Email Workaround
We then programmed our bot to visit each vendor’s individual profile page on Houzz and collect any relevant details available there. Unfortunately, as anticipated, no email address was listed directly on these pages. But, they did provide the vendor’s official website URL. This gave us our next target.

Vendors Website
Obtain Payload
Our bot was then directed to the vendor’s own website. We instructed it to scour the various pages of that site (commonly looking at “Contact Us,” “About Us,” or footer information) specifically in search of an email address – our desired “payload.”
Here is the PHP code for the bot’s logic, which you can access in the Bitbucket repository.
The MySQL database insertion code is commented out in the provided script. This is in case you prefer to store the retrieved data in a database rather than a file. In this specific implementation, I opted to place the results directly into a CSV file.
The script writes two CSV files:
1. One file used in Step 1 for indexing the vendor website URLs found on Houzz.
2. In Step 2, this CSV file of target vendor website URLs is then used as the input for the bot to search for email addresses on those external sites.
Feedback and progress updates are outputted in the terminal during the script’s execution using fwrite(STDOUT).
```php
//You can get these files over at my https://bitbucket.org/nicknguyenzrd/houzzbot/
require("crawler.php");
require("CSSQuery.php");
/* Uncomment below to store data in MYSQL
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "invoice";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
*/
//Step 1: Gather Houzz Links
//Open Links File because thats where well dump our data payload
$handle = fopen("links.txt", "r");
$id=94;
$type=1;
//Data Placeholder Array
$data['href']=array();
$data['company']=array();
$data['type']=array();
$data['id']=array();
$id=1;
//Deal with multiple page results with The All Powerful Iterative Loop
for ($i = 1; $i <= 30; $i++) {
$doc = new DOMDocument();
if($i===1) {
$p=0; //To grab the first page had a different URL
$doc->loadHTML( file_get_contents( "http://www.houzz.com/professionals/landscape-architect/orange-county"));
} else {
//Every Page after the first page "/p/{page number}"
$doc->loadHTML( file_get_contents( "http://www.houzz.com/professionals/landscape-architect/orange-county/p/" . $p ) );
}
//Webpage loaded for us
$css = new CSSQuery( $doc );
$arr = array();
$arr = $css->query( 'a.pro-title' );
foreach ( $arr as $a ) {
//Get URL Link Filter out Javascript
if ( $a->attributes->getNamedItem( 'href' )->value === "javascript:;" ) {
} else {
//Store link and company name
$data['id'][]=$id;
$data['href'][] = $a->attributes->getNamedItem( 'href' )->value;
$data['company'][] = $a->nodeValue;
$data['type'][]=1;
//Open our List of Links Page
$handle = fopen('links.txt',"a+");
$somecontent = $a->attributes->getNamedItem( 'href' )->value."\r\n"; // Use \r\n for Windows/Linux compatibility
fwrite($handle,$somecontent);
fwrite(STDOUT, $somecontent);
fclose($handle);
$id++;
}
}
$p=$p+15;
sleep(1);
unset($doc);
unset($css);
//var_dump( $data );
}
//Step 2: Gather company details (Houzz doesnt list email addresses), so well have to improvise and go to there website to acquire target email contact if its listed on there website.
//Make sure we double check were dealing with valid URLS cause that can really fuck things up once this bitch is fired up!
function get_valid_url( $url ) {
$regex = "((https?|ftp)://)?"; // Scheme
$regex .= "([a-z0-9+!*(),;?&=\$_.-]+(:[a-z0-9+!*(),;?&=\$_.-]+)?@)?"; // User and Pass
$regex .= "([a-z0-9-.]*).([a-z]{2,3})"; // Host or IP
$regex .= "(:[0-9]{2,5})?"; // Port
$regex .= "(/([a-z0-9+\$_-].?)+)*/?"; // Path
$regex .= "(\?[a-z+&\$_.-][a-z0-9;:@&%=+/\\$_.-]*)?"; // GET Query
$regex .= "(#[a-z_.-][a-z0-9+\$_.-]*)?"; // Anchor
return preg_match("/^$regex$/", $url);
}
if ($handle) {
while ( ( $line = fgets( $handle ) ) !== false ) {
$email="";
$website="";
$url="";
$name="";
$company="";
$phone="";
$link="";
$tier="";
$location="";
$license="";
$error="";
$sql="";
$doc = new DOMDocument();
// Suppress errors for malformed HTML
@$doc->loadHTML( file_get_contents( trim($line) ) ); // Trim whitespace from line
$css = new CSSQuery( $doc );
//Houzz Link to profile
$data['link']=$line;
$link=trim($line);
//Company Name
$nrr = $css->query( 'a.profile-full-name' );
if (isset($nrr[0]) && $nrr[0]->textContent) {
$data['company'][] = $nrr[0]->textContent;
$company=$nrr[0]->textContent;
fwrite(STDOUT, "Starting: ".$id.":".$nrr[0]->textContent."\r\n");
} else {
$company = "N/A";
fwrite(STDOUT, "Starting: ".$id.": Company Name Not Found\r\n");
}
//Website and Email Addresses TODO add conditional statement
$arr = $css->query( 'a.proWebsiteLink' );
$website_found = false;
foreach ( $arr as $a ) {
$url= $a->attributes->getNamedItem( 'href' )->value;
// Basic URL validation before attempting to crawl
if (filter_var($url, FILTER_VALIDATE_URL)) { // More robust URL validation
$data['website'][] = $url;
$website = $url;
$website_found = true; // Mark that a website was found
fwrite(STDOUT, "Attempting site: ".$url."\r\n");
// Simple email extraction - a real crawler would be more sophisticated
$site_content = @file_get_contents($url); // Use @ to suppress errors for unreachable sites
if ($site_content !== FALSE) {
// Regex to find email addresses
if (preg_match('/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/', $site_content, $matches)) {
$email = $matches[0];
// Output indicating email address discovered CLI
fwrite(STDOUT, "Found Email Address: ".$email."\r\n");
} else {
fwrite(STDOUT, "No Email Address Found on Site\r\n");
}
} else {
fwrite(STDOUT, "Could not retrieve content from site: ".$url."\r\n");
$error .= "Could not retrieve content from site; ";
}
// Only try the first valid URL found
break;
} else {
fwrite(STDOUT, "Invalid Website URL found: ".$url."\r\n");
$error .= "Invalid Website URL; ";
}
}
if (!$website_found) {
$website = "N/A";
fwrite(STDOUT, "No Website URL found on Houzz profile\r\n");
}
//Phone Number
$phone_found = false;
$crr = $css->query( 'span.pro-contact-text' );
foreach ( $crr as $c ) {
if($c->nodeValue!=="Website") { // Exclude the "Website" text itself
$phone = trim($c->nodeValue); // Trim whitespace
$data['phone'][] = $phone;
$phone_found = true;
break; // Assume only one phone number listed this way
}
}
if (!$phone_found) {
$phone = "N/A";
}
//All company details (Contact, Location, License, Tier)
$info = $css->query( 'div.info-list-text' );
$name = "N/A";
$location = "N/A";
$license = "N/A";
$tier = "N/A";
foreach ( $info as $i ) {
$text = trim($i->nodeValue); // Trim whitespace from text content
//Person to contact
if (strpos( $text, "Contact:" )!==FALSE) {
$name = str_replace( "Contact:",'', $text );
$name = trim($name);
$data['contact'][] =$name;
}
//Address/Location
if (strpos( $text, "Location:" )!==FALSE) {
$location = str_replace( "Location:",'', $text );
$location = trim($location);
$data['location'][]=$location;
}
//License Number
if (strpos( $text, "License Number:" )!==FALSE) {
$license=str_replace( "License Number:",'', $text );
$license=trim($license);
$data['license'][] =$license;
}
//Tier (Typical Job Costs)
if (strpos( $text, "Typical Job Costs:" )!==FALSE) {
$tier =str_replace( "Typical Job Costs:",'', $text );
$tier=trim($tier);
$data['tier'][]=$tier;
}
}
// Write architect contact information into a CSV file
$wr= fopen('archs.csv',"a+");
// Use fputcsv for proper CSV formatting and escaping
fputcsv($wr, [$id, $type, $company, $phone, $website, $email, $link, $name, $location, $license, $tier]);
//Disable Comment Below to OutPut to CLI
//fwrite(STDOUT, $details);
$id++;
fclose($wr);
/* Uncomment below if youd rather insert scrapped data into MySQL Database
$sql = "INSERT INTO ip_oppurtunities(`type`,`company`,`phone`,`website`,`email`,`link`,`contact`,`location`,`license`,`tier`)
VALUES (1,'$company','$phone','$website','$email','$link','$name','$location','$license','$tier')";
if ($conn->query($sql) === TRUE) {
fwrite(STDOUT, $id.'-'.$company." Added \r\n");
} else {
$error=mysqli_error($conn);
fwrite(STDOUT, "Error: ".$company."=[".$sql."]".$error."\r\n");
echo $error;
die(); // Consider logging error and continuing instead of dying
}
$id++;
*/
}
fclose($handle); // Close the links file after processing
// $conn->close(); // This should be outside the while loop if using DB insertion
}
We build a TV Channel
Introducing the Makers Network on ROKU
(Note: The video above introduces the concept of the Makers Network channel.)
Through the “Makers Network” project, we bridge the gap between digital expertise and traditional TV broadcasting. We achieve this by creating our very own dedicated TV channel on the ROKU platform, providing access to its massive network of over 40 million subscribers.
This approach offers a powerful alternative to traditional advertising. Instead of simply paying for ad space, appealing content on the Makers Network creates the potential for your brand to not only gain exposure but potentially earn revenue through marketing efforts.
We understand that viewers watching TV in the comfort of their homes are often more receptive to marketing messages. Content can be strategically created to promote your brand and sell products, both directly and indirectly, leveraging this engaged audience.
ROKU provides significant flexibility and various options for integrating advertising within your channel. We specialize in developing a proper, tailored strategy that maximizes your brand’s reach and impact on this platform.
Let us help you bring your brand’s story to the big screen!
Case Study: Combating Robocalls with Robo-Bouncer
Case Study: Introducing the RoboBouncer – AI-Powered Call Screening
In this case study, we demonstrate how we bring our internet and programming skills to traditional telecommunications through a project we’ve dubbed the RoboBouncer.
The RoboBouncer is an intelligent bot designed to screen your incoming voice phone calls. When a call comes in, the system asks the caller to state their name and records it. It then places the caller on hold while simultaneously making an outbound call to your personal phone number. You are played the recorded name, and the system provides you with a clear option to either accept or reject the call.
With full programmatic control over call handling logic, the possibilities that the internet brings to traditional telephony are virtually endless. Beyond the RoboBouncer, we have experience setting up sophisticated phone systems, including:
- Interactive Voice Response (IVR) systems (e.g., company directories).
- Advanced lead transfer systems.
- High-volume outbound calling solutions.
To personally test out the RoboBouncer service, you are welcome to call our dedicated phone number: (949) 446-1716.
Following popular demand, we have also developed a commercial version that automates the deployment and setup of your own RoboBouncer instance. Click here to create your own RoboBouncer easily.
For those who are technically inclined, we have made the code and detailed information available in our BitBucket repository.
Creating a telephone call screening service using Twilio
This project demonstrates the creation of a call screening service utilizing the Twilio platform.
The Call Flow:
- Caller calls your Twilio Number: The system (the “agent”) answers and prompts the caller to speak their name, which is then recorded. The caller is subsequently placed in a queue (representing the “Inbound Call”).
- Outbound Call to You: Using the Twilio REST API, the system initiates an outbound call from your Twilio Number to your designated personal phone number.
- Playback and Options: When you answer, the recorded name of the caller is played back to you. You are then presented with the following options:
- Press 1 (DTMF) or Say “Accept” (SpeechResult): This action uses Twilio’s Dial verb to connect your phone to the caller who is waiting in the queue.
- Press 2 (DTMF) or Say “Reject” (SpeechResult): Using the REST API, the system notifies the waiting caller (e.g., with a message like “The person you are trying to reach is unavailable”) and then ends the call for both parties (hangs up on the caller in the queue and hangs up on your outbound call).
Technical Details:
- Uses the TWIML API and the Twilio Voice API PHP SDK.
- Utilizes the Queue resource and the Enqueue TwiML verb to place the caller on hold while the outbound call to you is made. Note: You could also use the Conference verb instead of Enqueue for potentially more options, simply by replacing Enqueue with Conference in the TwiML.
- To get started, follow this guide to set up your free Twilio account, get a phone number, and obtain your API SID and Token: https://www.twilio.com/docs/voice/quickstart/php
- Remember to configure the webhook for your Twilio phone number to point to your
step1.xmlfile (or select the appropriate TwiMLBin URL if using that).
Deployment Suggestion:
Setting up a dedicated web server isn’t strictly necessary. You can leverage Twilio’s own hosting features:
- Use Twilio TwiMLBins to host your XML files (like
step1.xml). Let Twilio handle the file hosting for you. - Use Twilio Functions for your server-side logic (converting the PHP logic to Javascript). The IVR Menu example in the Twilio documentation is a good starting blueprint for this.
I would strongly suggest using TwiMLBins for hosting XML files and, if you are capable, utilizing Twilio Functions for the server-side code to simplify your deployment.
Case Study: Native West
Helping Native West Launch and Grow with Comprehensive Tech Solutions
We provided a wide array of technical solutions to help Native West successfully start their business and establish a strong digital presence. Our services included IP Phone Management, a custom Bidding System, and not only designing their website but also optimizing it to rank at the top of search engines.
Website Design
IT Services was responsible for both the design and development of the main Native West website.


SEO
Achieving Additional Site Links in Search Results
The term “Native West” presented a significant challenge for search engine optimization initially, as it was heavily dominated by content related to Native American topics. However, through proper site structure optimization and targeted SEO strategies, we quickly achieved the top spot in search engine results. Furthermore, their listing displayed with valuable additional site links, giving them increased visibility and click-through potential.
Phone System
Managed Phone System
We implemented a fully managed IP Phone System for Native West. This provided them with complete control and flexibility to customize call flow, directing calls efficiently to field agents, specific departments, or voicemail as needed.


Management Platform
Bidding and Contract System
We developed a custom Management Platform that serves as the company’s operational backbone. This system is responsible for coordinating and connecting various departments – including Sales, Construction, Accounting, and Management – throughout the many complex stages of landscape construction projects, from initial bidding to final accounting.
Want more control over your company?
Schedule a free phone consultation to discuss how our solutions can help you streamline operations and gain greater control.
Is your account info compromised?
Demonstrating Data Security Risks: A Look at Compromised Credentials
We have curated a significant collection of over 1.5 billion hacked credentials (including usernames and associated passwords) sourced from various parts of the internet, including the “Deep Web/Dark Web” (often referred to as the internet underground). This substantial trove of data now totals over 40 Gigs – roughly equivalent to the storage capacity for approximately 13,000 songs on an MP3 player. As you can imagine, processing and managing data on this scale requires techniques far beyond typical web programming.
We hope you find the following functional demonstration both useful and informative. Even we were quite surprised to discover that a couple of our own accounts had been compromised based on this data.
To prevent any potential abuse, we can only show you a portion of the compromised password associated with any queried username/email.
Important Note: We strongly dislike SPAM. Any email addresses provided during this demonstration will not be redistributed or used for any other purpose.






