<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Tukri]]></title><description><![CDATA[Tukri]]></description><link>https://tukri.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6ae35f9ce5d6eb2076aa283a/7e6895c6-338f-4ea9-92e5-a3dceb9542db.png</url><title>Tukri</title><link>https://tukri.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 31 Oct 2026 12:48:15 GMT</lastBuildDate><atom:link href="https://tukri.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What broke when an AI agent filled in 50 sign-up forms for us]]></title><description><![CDATA[Notes from filling in 50 directory sign-up forms with an AI agent: a 200 that meant failure, odd controls, and where it stopped on purpose.]]></description><link>https://tukri.hashnode.dev/what-broke-when-an-ai-agent-filled-in-50-sign-up-forms-for-us</link><guid isPermaLink="true">https://tukri.hashnode.dev/what-broke-when-an-ai-agent-filled-in-50-sign-up-forms-for-us</guid><category><![CDATA[automation]]></category><category><![CDATA[startup]]></category><category><![CDATA[bangladesh]]></category><category><![CDATA[ai agents]]></category><dc:creator><![CDATA[Tukri]]></dc:creator><pubDate>Sat, 31 Oct 2026 01:23:05 GMT</pubDate><content:encoded><![CDATA[<p>At the end of October we put Tukri, a Bangla point-of-sale and dues-book app for small shops, on a list of 50 directories and launch sites and tried to get it listed on all of them. The scoreboard is in <a href="https://dev.to/tukri/we-submitted-a-bangla-pos-app-to-50-directories-here-is-what-actually-happened-5bpa">a separate post on dev.to</a>. This post is the engineering notes: what an AI agent driving a real browser tripped over, and where it stopped on purpose.</p>
<h2>The setup</h2>
<ul>
<li>An AI coding agent drove a dedicated Chrome profile for the company through the Chrome DevTools protocol. It reads the page's accessibility tree, then clicks and types by element id.</li>
<li>The rules: free plans only; Google sign-in first, email and password otherwise; sign-in codes read from the company inbox; no reviews, no votes, no invented numbers.</li>
<li>Every site got one row in a table: how we signed in, a status, and the one step left. Passwords went to a local file outside the repository, never into the table or the Git history.</li>
</ul>
<h2>1. A 200 that meant failure</h2>
<p>G2's Add Product form takes a name, a domain, a description and a logo. The POST carried every field and came back with HTTP 200, and the page said "Something went wrong while saving your product". The status code looked healthy.</p>
<p>An automation that waits for a success phrase is easy to fool here. One of our waits matched G2's own "conditionally approved" boilerplate and looked like success, until a screenshot showed the error banner. Assert on something only a success can produce, such as a new URL or a record id, and look at the page when it matters.</p>
<p>We tried six times from a clean start with the same result. Only G2 can say why.</p>
<h2>2. A field that was blank after the lookup</h2>
<p>On the same form, the Vendor Name was empty after the domain lookup finished, although we had filled it. Typing it again with real keyboard events, after the lookup, kept it.</p>
<h2>3. Controls that are not the input</h2>
<ul>
<li><strong>Styled radios.</strong> The radio's own element would not take a click. Its label did.</li>
<li><strong>A Vue multiselect</strong> on a project form (the tech-stack field). Focusing its hidden search input with a script, typing the value and pressing Enter selected it.</li>
<li><strong>File uploads.</strong> The browser tool only accepts files inside the project directory, so the screenshots were staged in a git-ignored folder first.</li>
</ul>
<h2>4. A .bd domain that a validator would not accept</h2>
<p>StartupBlink refused our submission with "Your account email does not use the tukri.bd domain, so we cannot confirm you represent this company", although the account email is <a href="mailto:info@tukri.bd">info@tukri.bd</a>.</p>
<p>Our first guess was the Public Suffix List, the list that tools use to decide where a registrable domain starts. Rather than assume, we checked the current list:</p>
<pre><code class="language-sh">curl -s https://publicsuffix.org/list/public_suffix_list.dat | sed -n '/^\/\/ bd :/,/^$/p'
</code></pre>
<p>It names <code>bd</code> and a fixed set of second levels: <code>ac.bd</code>, <code>ai.bd</code>, <code>co.bd</code>, <code>com.bd</code>, <code>edu.bd</code>, <code>gov.bd</code>, <code>id.bd</code>, <code>info.bd</code>, <code>it.bd</code>, <code>mil.bd</code>, <code>net.bd</code>, <code>org.bd</code>, <code>sch.bd</code> and <code>tv.bd</code>. There is no wildcard, so <code>tukri.bd</code> is not a public suffix, and a validator using the current list would treat it as an ordinary registrable domain. The guess was wrong, at least for today's list.</p>
<p>We still do not know why StartupBlink rejected it. G2's save error is on the same domain, which may be a coincidence. We have left both for their support teams. If you have seen a <code>.bd</code> domain trip a validator, we would like to know what it was.</p>
<h2>5. Where the agent stopped on purpose</h2>
<ul>
<li><strong>CAPTCHAs and bot walls.</strong> Nine sign-ups ended at an image puzzle, a "verify you are human" box, a bot-block page, or a sign-in that looped back to itself. We did not try to get around them.</li>
<li><strong>Personal declarations.</strong> Boxes such as "I am 16 or older" and "I declare that this information is truthful" were refused by the agent's permission system when ticked on a person's behalf, even after a blanket "do everything as me". The ticks that had been approved by name (Terms and Privacy for one site) went through. We think that is right: a statement made under a person's name is theirs to make.</li>
</ul>
<h2>6. The log is worth more than the clicks</h2>
<p>"Submitted" is not "live", and "account made" is not "listed". Each row says what exists, what does not, and the one step left. That is how we know that 13 submissions are waiting in review queues, instead of claiming 13 listings.</p>
<p>It also made a mistake cheap to fix. Our first screenshots came from the repository and showed Bangla product names and the taka sign, which do not belong on English listing pages. Because every site had a row, we could find each place they had been uploaded and replace them with English-only images.</p>
<p>Tukri is at <a href="https://tukri.bd">tukri.bd</a>, with a free plan.</p>
<p><em>Disclosure: we are the Tukri team. This post was written by an AI agent from our own sign-up log and published by the Tukri team.</em></p>
]]></content:encoded></item><item><title><![CDATA[Free CSV templates for a shop's dues ledger (bakir khata) and product list]]></title><description><![CDATA[Many small shops in Bangladesh still keep their dues in a paper bakir khata (a customer dues ledger), and their product list in a notebook. A spreadsheet…]]></description><link>https://tukri.hashnode.dev/free-csv-templates-for-a-shop-s-dues-ledger-bakir-khata-and-product-list</link><guid isPermaLink="true">https://tukri.hashnode.dev/free-csv-templates-for-a-shop-s-dues-ledger-bakir-khata-and-product-list</guid><category><![CDATA[csv]]></category><category><![CDATA[google sheets]]></category><category><![CDATA[bangladesh]]></category><category><![CDATA[Small business]]></category><dc:creator><![CDATA[Tukri]]></dc:creator><pubDate>Thu, 29 Oct 2026 15:26:59 GMT</pubDate><content:encoded><![CDATA[<p>Many small shops in Bangladesh still keep their dues in a paper bakir khata (a customer dues ledger), and their product list in a notebook. A spreadsheet is a good first step up from that, and you don't need any app or sign-up to start.</p>
<p>Here are two plain CSV templates you can open in <strong>Google Sheets</strong> or <strong>Excel</strong> today:</p>
<table>
<thead>
<tr>
<th>File</th>
<th>What it is for</th>
</tr>
</thead>
<tbody><tr>
<td><code>baki-khata-ledger.csv</code></td>
<td>A customer dues ledger (bakir khata): who took what on credit, what they paid back, and what they still owe</td>
</tr>
<tr>
<td><code>tukri-product-starter.csv</code></td>
<td>A product list (name, Bangla name, price, stock) in a format the Tukri app can import as-is</td>
</tr>
</tbody></table>
<p>You can get both files from this public gist: <a href="https://gist.github.com/tarikmanoar/e932cb88f0bd95334b257175f51bc787">https://gist.github.com/tarikmanoar/e932cb88f0bd95334b257175f51bc787</a></p>
<p>Both files are saved as <strong>UTF-8</strong>, so Bangla text survives. In Excel, open them with <em>Data &gt; From Text/CSV &gt; File origin: 65001 (UTF-8)</em> if the Bangla looks broken.</p>
<h2>1. The dues ledger (<code>baki-khata-ledger.csv</code>)</h2>
<p>One row per event: a credit sale <strong>or</strong> a payment, never both.</p>
<table>
<thead>
<tr>
<th>Column</th>
<th>Meaning</th>
</tr>
</thead>
<tbody><tr>
<td><code>date</code></td>
<td>The day it happened (YYYY-MM-DD sorts correctly)</td>
</tr>
<tr>
<td><code>customer_name</code>, <code>customer_phone</code></td>
<td>Who. Keep one spelling per customer, so their rows group together</td>
</tr>
<tr>
<td><code>description</code></td>
<td>What was taken, or "Cash payment" / "bKash payment"</td>
</tr>
<tr>
<td><code>credit_given_taka</code></td>
<td>The amount given on credit (baki), or 0</td>
</tr>
<tr>
<td><code>payment_received_taka</code></td>
<td>The amount paid back (joma), or 0</td>
</tr>
<tr>
<td><code>running_balance_taka</code></td>
<td>What this customer still owes after this row</td>
</tr>
<tr>
<td><code>promised_date</code></td>
<td>When they said they would pay</td>
</tr>
<tr>
<td><code>note</code></td>
<td>Anything worth remembering</td>
</tr>
</tbody></table>
<h3>Let the sheet do the running balance</h3>
<p>In Google Sheets, with the header in row 1 and the data from row 2, put this in <code>G2</code> and fill it down. It totals that customer's credit minus their payments up to the current row:</p>
<pre><code class="language-plaintext">=SUMIFS($E$2:E2,$B$2:B2,B2)-SUMIFS($F$2:F2,$B$2:B2,B2)
</code></pre>
<p>For everyone's total due on one line:</p>
<pre><code class="language-plaintext">=SUM(E2:E)-SUM(F2:F)
</code></pre>
<p>For one customer's current due, with the name in <code>J1</code>:</p>
<pre><code class="language-plaintext">=SUMIFS(E2:E,B2:B,J1)-SUMIFS(F2:F,B2:B,J1)
</code></pre>
<h3>Habits that make a ledger work</h3>
<ol>
<li><strong>Write the due down the moment you give it.</strong> A due added at night from memory is a due that's half-remembered.</li>
<li><strong>One customer, one spelling.</strong> "Karim Mia" and "Karim bhai" become two people to a formula.</li>
<li><strong>Always ask for a date.</strong> "Next week" becomes <code>promised_date</code>, and you sort by it every morning.</li>
<li><strong>Record part payments.</strong> A customer who pays 500 taka of 910 should see 410 taka left, not "still owes".</li>
<li><strong>Share the history, not only the total.</strong> A customer who sees the dated rows argues less than one who hears a number.</li>
</ol>
<h2>2. The product list (<code>tukri-product-starter.csv</code>)</h2>
<p>These are the eleven columns of the <a href="https://tukri.bd">Tukri</a> product importer:</p>
<pre><code class="language-plaintext">sku,name,name_bn,category,unit,barcode,cost_price,sale_price,stock_qty,low_stock_threshold,is_active
</code></pre>
<ul>
<li><strong>Required:</strong> <code>name</code>, <code>unit</code> (<code>Pieces</code> or <code>pcs</code>), <code>cost_price</code> and <code>sale_price</code>.</li>
<li><strong>Money:</strong> taka with up to two decimals and no symbol, so <code>210.00</code>, not a currency sign in front of it.</li>
<li><code>is_active</code><strong>:</strong> exactly <code>YES</code> or <code>NO</code>.</li>
<li><strong>The unit</strong> must already exist in the shop; the importer never invents one.</li>
<li><strong>Leave unknown cells empty.</strong> Don't guess barcodes or stock.</li>
</ul>
<p>The full column list covers variants (sizes and colours), batches and expiry dates, packs (a box of 12) and warranties. It's in the public guide, along with a ready-made file:</p>
<ul>
<li>Guide: <a href="https://tukri.bd/products/import-guide">https://tukri.bd/products/import-guide</a></li>
<li>Full template: <a href="https://tukri.bd/products/import-template.csv">https://tukri.bd/products/import-template.csv</a></li>
</ul>
<h2>When a spreadsheet stops being enough</h2>
<p>A sheet is a good start. It gets hard when:</p>
<ul>
<li>several people write in it at once;</li>
<li>you want to text a customer their exact due from your phone;</li>
<li>you need to bill at the counter while the internet is down.</li>
</ul>
<p>That's the gap <strong>Tukri</strong> fills. It's a Bangla-first POS, stock and dues-book app for small shops in Bangladesh, and it runs in the phone's browser and works offline. Its <strong>Free plan includes the digital bakir khata</strong>.</p>
<ul>
<li>Digital dues book: <a href="https://tukri.bd/tali-khata">https://tukri.bd/tali-khata</a></li>
<li>How to track customer dues: <a href="https://tukri.bd/learn/track-customer-dues">https://tukri.bd/learn/track-customer-dues</a></li>
<li>Plans and limits: <a href="https://tukri.bd/pricing">https://tukri.bd/pricing</a></li>
</ul>
<p><em>Disclosure: this article and the templates come from the Tukri team, the people who build Tukri. The templates are free to use, copy and change. No sign-up is needed for the CSV files.</em></p>
]]></content:encoded></item></channel></rss>