Portfolio

Work

Shipped products, current builds, and personal projects across commerce, AI, banking, and developer experience. Tap any project to see the full story.

Compliance Hub is Swap's system of record for product classification: the tax codes and HS/HTS codes that decide how much duty and tax a cross-border shopper pays at checkout. I led two workstreams that moved classification from manual and partial to automated and destination-specific.

Challenge: shoppers were charged the wrong duty and tax because classification was manual, partial, and blind to what the product actually was. Solution: made Compliance Hub the source of truth, classifying only active products on change with full product context, synced into the fields checkout already reads. Impact: +40% duty/tax accuracy and ~60% less manual SKU review across 13K+ jurisdictions.

The problem

  • Transactions reached our tax engine without product-level tax codes, so it fell back to standard rates. That meant systematic over-collection: shoppers in markets with reduced rates or exemptions were charged more than the law required, inflating checkout prices and quietly costing conversion.
  • For duties, we passed merchant 6-digit HS codes to the tax engine, which extended them to destination-specific 8 or 10 digits using only the partial code and the destination country, ignoring product title, description, and material composition. For jewelry, religious items, and mixed-material goods, that produced wrong classifications.
  • We had no baseline for how wrong we were. The gap stayed invisible until a jewelry brand, with roughly 5,000 SKUs under manual review, flagged the discrepancies and stalled onboarding over customs accuracy. The complaint was the first real signal we had.

How I measured success

  • Classification coverage: share of active SKUs with destination-specific HS codes.
  • Merchant complaints: volume of HS code-related support tickets and onboarding concerns.
  • Duty accuracy: duty calculation disputes tied to HS codes.

What I did

  • Made Compliance Hub the source of truth for classification, with two coordinated pipelines, since tax codes and customs codes can't come from a single API.
  • Ran classification asynchronously on product create/update, only for active products and only on change, so checkout never waits on a real-time third-party call and classification cost doesn't scale with the whole catalog.
  • Fed full product context (title, description, category, material composition) into classification to generate accurate destination-specific codes, and let merchants configure target markets and a default.
  • Synced generated codes into Shopify's market-specific HS fields, where checkout already reads them, so the change needed no checkout rewrite.

The alternative I rejected

The obvious build was our own fine-tuned HS classification model. We tried it. Against the tax engine's classification API fed with full product context, our model trailed by about 15 points on accuracy. In a regulated domain that gap is the difference between a correct customs invoice and an audit liability, so I chose to orchestrate the existing API with better inputs rather than own the model, and put the effort into the data contracts and sync that actually moved accuracy.

The lesson

The breakthrough on the jewelry disputes was unglamorous: running the classification API with full product depth as input returned the exact HTS codes the merchant said were right. The accuracy problem wasn't the model, it was the inputs we were feeding it. The trade-off I designed around: making Compliance Hub a checkout dependency turned sync freshness into a reliability concern, and per-call costs scale with catalog size, so classifying only active products on change was the lever for both.

What I'd do differently

We found the problem the worst possible way, through a merchant complaint, because we had no view into accuracy before brands flagged it. If I ran it again, I'd ship the coverage and dispute instrumentation before the classification pipeline, not after. Knowing destination-specific code coverage and HS dispute rate from day one would have surfaced the jewelry misclassifications in-house, instead of in a stalled onboarding.

+40%
Duty/tax accuracy
~60%
Less manual SKU review
13K+
Jurisdictions in scope

Results showed up immediately after launching with correct destination-specific duties, across the ~5,000 active SKUs that had been under manual review.

Proof in the wild

Red Equipment, a UK outdoor brand, rebuilt cross-border fulfillment on Swap, including the transparent duties and territory-specific tax/VAT this work powers, and reported +111% global sales YoY, +65% conversion, and +13% AOV. That's a platform-level outcome with many inputs; accurate, surprise-free duty and tax at checkout is one of them. Read the case study →

Challenge: support split shipments (orders fulfilled from multiple warehouses across countries) where duties and tax calculate by warehouse country of origin, not by order. Solution: shipped tax and duty first, then built the per-line shipping calculator the feature actually needed to be usable. Impact: went from a launch almost nobody could activate to a feature sales now leads with.

Situation
We needed to support split shipments, orders with items from multiple warehouses across countries. The high-stakes part was customs: taxes and duties calculate by warehouse country of origin, not by order, because customs invoices reflect country-of-origin liability. It was the top-requested feature from our largest enterprise merchants.
The call
The challenge had two layers: line-item tax and duty (the visible problem), and line-item shipping fees (the hidden one). Shopify calculates shipping at order level, not line level, which made per-line shipping fees much harder. I made the call to ship the tax-and-duty solution fast and launch with free-shipping-only as the supported scenario. My logic: tax and duty was the bigger pain, we'd handle shipping later. I convinced brands and sales to go along.
Recovery
After launch, brands signed up excited, then couldn't use the feature. Most merchants don't offer free shipping. The feature shipped, and almost nobody activated. So we went back and built the harder problem: a per-line shipping fee calculator inside the Swap portal, since Shopify couldn't support it. Merchants could define their own shipping criteria for split-shipment orders. It took weeks longer than the original launch. We re-released, and it's now a feature sales leads with.
The lesson

"Edge case" is a misleading term in niche or regulated domains. The edge is often what determines whether the feature is usable at all. I traded edge-case coverage for launch speed. The trade was wrong, because the edge wasn't the edge. It was the use case for most of the customer base.

What I'd do differently

Now I scope down the audience, not the feature. Before a release I ask what's the smallest scope where the whole flow works, not just the headline. When I shipped the LLM customs description generator later, I limited launch to clothing only and kept jewelry, footwear, and medical devices out until we'd evaluated composition accuracy. Same trade-off as split shipments, resolved the right way.

I shaped Swap's U.S. sales tax product and wrote the public guide that explains it. The work turns a problem most founders avoid into something operators can actually act on.

Challenge: U.S. sales tax isn't one tax. It's 12K+ jurisdictions and 50+ states with separate filings, and post-Wayfair economic-nexus rules make a brand liable the moment it crosses a state's sales or order threshold, often without realizing. Solution: nexus tracking across all 50 states, one-click registration, and audit-ready filing, with a public guide that explains the whole thing. Impact: rate handling from 0 to 11.25% automated, and a compliance problem founders avoid turned into a guided workflow.

The problem

  • After South Dakota v. Wayfair, physical presence stopped being the test. Economic nexus means crossing a state's sales or order volume makes a brand liable to register and file there, and the thresholds differ by state.
  • That leaves 50+ separate filing regimes and 12K+ local jurisdictions, each with its own rate. Most founders don't track when they cross a threshold, so they find out late, after the liability has accrued.

What I did

  • Shaped a product that tracks nexus across all 50 states and flags when a brand approaches or breaches a threshold.
  • Added one-click registration and audit-ready filing, so crossing a threshold leads to action instead of a scramble.
  • Wrote the public guide that explains nexus, thresholds, and filing in operator language, turning the product's logic into something a founder can act on.
12K+
Tax jurisdictions in the U.S.
50+
States needing separate filings
0–11.25%
Rate range handled automatically

Read the guide →

As Product Manager at OCBC, I owned Connect2OCBC, the bank's open-banking portal where fintech partners discover, test, and integrate against OCBC's 500+ APIs. I ran it across the lifecycle, from requirements to launch.

Challenge: a bank can publish APIs and still see little adoption if partners can't find the right one, try it safely, and go live without a sales conversation for every integration. OCBC's open-banking presence sat in InnoPay Quadrant 3. Solution: a self-serve portal with a clear path: get access, experiment in a sandbox, then go live. Impact: InnoPay ranking moved from Quadrant 3 to Quadrant 4 and targeted API consumption grew 10%.

The problem

  • OCBC had a large API surface but no easy front door. Partners couldn't discover the right API, test it against realistic data, or move to production on their own.
  • Without a self-serve path, every integration leaned on hand-holding, adoption lagged, and the bank's open-banking standing sat in InnoPay Quadrant 3.

What I did

  • Owned Connect2OCBC across the product lifecycle: concept docs, PRDs, mockups, and backlog and sprint priorities with the Scrum team.
  • Built the three-step path partners actually use: get access and create an app, discover and experiment with APIs in a sandbox, then test and go live.
  • Worked with business stakeholders and support functions to manage change impact, and partnered with clients to drive sales through the API offerings.
Q3 → Q4
InnoPay open-banking ranking
+10%
Targeted API consumption
500+
APIs available to partners

Visit Connect2OCBC →

A personal build: an agentic "Proactive Delivery Agent" that watches orders in flight, detects delivery exceptions early, and takes or recommends action before they turn into support tickets. It's where I'm sharpening hands-on agentic-system design, the model-native, decision-making layer rather than AI-assisted features.

The idea, on paper

Proactive Delivery Agent concept sketch A hand-drawn sketch: order, carrier, and customer data feed an agent that watches orders, spots an exception, reasons about it, and acts or recommends, so problems are fixed before they become support tickets. Proactive Delivery Agent rough concept sketch orders in flight carrier tracking events order + customer data THE AGENT 1 - watch orders, always 2 - spot an exception (delay / stuck / bad address) 3 - reason: how bad? next? 4 - act, or recommend ping customer early nudge carrier / reroute alert ops, with context fixed before it's a support ticket reactive way = customer complains first :(

In active development. More detail and results to come.

No projects in this category yet.