SDKs Are Products: How We Built HT Booknow
An SDK is unlike most software you build. You don't control the environment it runs in. You don't control the browser, the framework, or the version of dependencies the host page is using. Your consumers interact with it through a contract — a public API — and everything behind that contract is invisible to them.
HT Booknow was an embeddable tractor booking widget for Hello Tractor. The goal: partner websites (agricultural platforms, rural services portals) could drop in a script tag and get a fully functional booking flow — calendar selection, tractor availability, payment — without implementing any backend integration themselves.
It contributed to a 34% improvement in booking conversion rate. Here's what building it like a product looked like.
Design the Interface Before the Implementation
The first thing we did was write the consumer documentation — before writing a single line of implementation. What would the script tag look like? What configuration options would exist? What events would the SDK emit? What methods would be exposed for programmatic control?
<script src="https://cdn.hellotractor.com/booknow/v1/widget.js"></script>
<script>
HTBooknow.init({
apiKey: 'YOUR_API_KEY',
containerId: 'ht-booking-widget',
locale: 'en',
onBookingComplete: function(bookingId) { ... },
onError: function(error) { ... }
});
</script>
This exercise surfaced questions we hadn't considered: How does a partner pass their own branding? What happens if the container element doesn't exist yet? What if init is called multiple times? Answering these before writing code kept the implementation honest.
Isolation Is Everything
The SDK runs on pages you don't control. Those pages may have their own CSS frameworks, global variable names, event listeners, and JavaScript libraries. Your SDK must not interfere with any of them.
Key isolation decisions we made:
- Shadow DOM for the widget's UI — complete CSS isolation, no leakage in either direction
- Scoped global — the SDK exposed exactly one global,
HTBooknow, and nothing else. No globals for internal state, utilities, or vendor libraries - Bundled dependencies — we didn't assume the host page had React, axios, or any other library. Everything the SDK needed was bundled and self-contained (using a unique internal scope to avoid conflicts with host-page React instances)
Distribution via S3 and CloudFront
The widget bundle was hosted on S3 and served through a CloudFront distribution. This gave us:
- Versioned URLs —
v1/widget.js,v2/widget.js. Partners pinned to a major version and got patch updates transparently, but breaking changes never surprised them - Cache-Control headers — immutable caching for versioned assets, short TTLs for the version alias (
latest/widget.js) - Global edge delivery — sub-50ms load times for partners in Africa, Europe, and the Middle East
- Signed URLs for enterprise partners who needed access control on the distribution
We also used Sentry for error tracking within the SDK. When something went wrong on a partner's page, we got the full context — browser, host page URL, SDK version, and the error stack — without exposing that data to the partner.
The Admin UI: Closing the Loop
The SDK was the consumer-facing side. The Admin UI was the operational side — used by Hello Tractor's internal team to make the system work at scale.
The most impactful feature was booking-to-schedule pairing: a dashboard that showed incoming bookings alongside available tractor event schedules, with an algorithm that suggested optimal pairings based on location, tractor availability, and booking timing. Admins could accept suggestions or manually reassign.
Before this dashboard, pairing was done manually via spreadsheets and phone calls. The dashboard made it a drag-and-drop operation. Combined with the SDK's lower friction booking flow, this is where the 34% conversion improvement came from — not just acquiring bookings, but actually fulfilling them efficiently.
Versioning and Backwards Compatibility
An SDK without versioning discipline is a liability. Every partner who embeds your widget is depending on you not breaking their site.
Our versioning contract:
- Patch versions — bug fixes, performance improvements. Deployed automatically to the pinned major version alias
- Minor versions — new optional configuration options or events. Backwards compatible, deployed to the alias
- Major versions — breaking changes to the public API. Never deployed to an existing alias. Partners opt in explicitly
We maintained a changelog and deprecation notices in the SDK's documentation, and emitted console.warn messages for deprecated configuration options before removing them.
Closing Thoughts
The 34% conversion improvement wasn't just about a better UI. It was about making the whole system — from booking initiation to tractor dispatch — work reliably. The SDK lowered the top-of-funnel friction. The Admin UI improved operational throughput. Both were necessary.
If you're building an embeddable SDK or a platform integration, think of it as a product with consumers, a contract, and a versioning strategy. Reach out if you want to talk through the design.