Welcome to Braedyn Thompson's portfolio!
See the roles I'm targeting →

Now playing
Berkeley Lab SeismicSoCal BearLM
INTERNSHIP · FULL-STACK

NumIsToken

Full-Stack Software Engineering Intern. I made token redemptions recoverable, built the product serialization tool for admins, and cut redemption action load times by about 85%.

Sep 2025 – Jun 2026Berkeley, CABackend · Docker
relative load time · redemption actions1.00before0.12after
NumIsToken home screen
Live siteThe NumIsToken home screen: tokenize a coin or go to OpenSea, with live gold and silver prices in the top bar.

Overview01

-85%Redemption load time~2–3 s → ~300 ms
0Unrecoverable stuck redemptionsa whole failure class removed
NUMISerial + barcode labelsadmin serialization tool
DockerProduction packagingcontainerized + deployed

  • 01

    Enabled resumable redemptions for previously unrecoverable transactions by building Redemption History and Detail services with robust state management and payment-resumption logic, eliminating a class of stuck-transaction failures.

  • 02

    Reduced redemption action load times by ~85% (~2–3 s to ~300 ms) by removing redundant function calls to streamline backend lifecycle APIs, then containerizing and deploying backend services with Docker for production readiness.

Resumable redemptions02

A redemption is a multi-step transaction: the user starts it, a payment step runs, and tokens are redeemed. If anything failed between steps (a dropped connection, a closed tab, a provider timeout), the transaction got stuck. There was no record a user or the system could use to finish it.

Before this, an interrupted redemption was stuck for good.

Resumable redemption (simplified illustration)REQUESTEDuser startsPAYMENT PENDINGprovider callPAYMENT SETTLEDfunds confirmedCOMPLETEtokens redeemedINTERRUPTEDwas: stuck foreverresume from lastdurable stateHistory + Detail services read thepersisted state, so the client canpick up where a failed attempt left off
IllustrationSimplified illustration of the idea, not the production schema: every step's state is persisted, so an interrupted redemption can be found and resumed from its last durable state.

What I built

  • Redemption History service: a durable, queryable record of each user's redemptions and where each one stands.
  • Redemption Detail service: the full state of one redemption, with enough context to resume it.
  • Payment-resumption logic: continue an interrupted redemption from its last confirmed state instead of restarting it or abandoning it, guarding against acting twice on the same payment.

The result is that a whole class of failures disappeared. It wasn't one bug fix. The system can now recover from any interruption between steps.

The history views

These are the style guides for the account's history views: a list of past submissions with their status, and a detail page that tracks each one through Submitted → Received → Missing Documents → Minted, flags any action the user needs to take, and lists the items and return address.

Submission history list style guide
Style guideSubmission history: each submission with its ID, item, quantity, date and status. Tabs switch between wallet info, submission history and redemption history.
Submission detail style guide
Style guideSubmission detail: a status tracker, an action-needed alert when documents are missing, the item table and the return address.

Style-guide mockups with placeholder data, not production screenshots. Open the interactive versions: history list · submission detail.

Product serialization tool03

Every physical item that becomes a NumisToken (a graded coin, a medal or a bullion bar) needs a unique serial number and a physical barcode label. I built the product serialization tool admins use to do both in one step.

Production product serialization screen
ProductionThe production serialization screen for admins: product details and the submission item ID, then the serial, its Code 128 barcode and the ZPL for the Zebra printer. Preview format uses a placeholder sequence and allocates nothing; Assign to item saves the serial on the submission item and advances the counter; Load label reprints an existing serial.

One form in; a unique serial, a barcode label and printer-ready ZPL out.

I designed the screen first as a standalone mockup, then built it into the admin app:

Serialization screen design mockup
Design mockupThe original design mockup: the same inputs, a 4″ × 2″ label preview and the ZPL output, laid out side by side.

How a serial is built

NUMI-{Category}-{Material}-{Weight}-{OriginalSN}-{Sequence} // e.g. NUMI-C-G-1OZ-SN78432-0000000001 (coin · gold · 1 oz · grading-cert SN · 1st in its prefix)
  • Category and material codes: C/M/B for coin, medal or bar, and G/S/P/C for gold, silver, platinum or copper.
  • Weight as a value plus unit (OZ, G or KG).
  • Original serial number from the grading certificate or mint (1–20 letters and digits), or NOSN for ungraded items.
  • A sequence number, zero-padded to 10 digits and counted separately for each category + material prefix. It's only allocated when a serial is assigned to a submission item, so previews never burn numbers.

From serial to printed label

  • A live 4″ × 2″ label preview with a Code 128 barcode of the serial.
  • ZPL output for Zebra label printers, with copy to clipboard.
  • Load label to reprint the label for a serial that already exists.
  • Validation on required fields and the serial-number format, so a bad label can't be printed.

Does previewing a label use up a serial number?

Nope. Only Assign to item allocates the next number in the sequence.

So nobody wastes serials just by looking.

Open the interactive design mockup (front-end only: its sequence counter runs in the browser, while the production tool allocates it on the backend).

~85% faster redemption actions04

Redemption actions felt slow, taking two to three seconds per action. Profiling the lifecycle APIs showed the time wasn't going into real work. It went into redundant function calls: the same lookups and checks repeated across the lifecycle of a single request.

Redemption action load time (lower is better)Before~2–3 sAfter~300 ms (−85%)0 s1 s2 s3 s
FigureStreamlining the backend lifecycle APIs took redemption actions from about 2–3 seconds to about 300 ms, roughly an 85% reduction.

Then: production-ready packaging

With the services faster and recoverable, I containerized the backend services with Docker and deployed them. That gave the same build in development and production, reproducible environments, and a clean path to deploy.