Welcome! Plan for about 2 hours of focused work. The project is intentionally larger than what most candidates finish in that time — we want to see how you scope the problem, what you choose to build first, and how you communicate about what you skip. A working partial solution is better than a broken complete one.
Click the green Use this template button at the top of this repo → Create a new repository → set it to Private → click Create.
Our Docker dev box has Claude Code, Python, Node, and Go pre-installed and pre-configured for you. From the directory where you cloned your new repo:
docker run -it \
-e ANTHROPIC_API_KEY="<your-api-key>" \
-v $(pwd):/workspace \
-p 8080:8080 \
ghcr.io/fedtax/interview-devbox:latestReplace <your-api-key> with the key from your onboarding email. Inside the
container, run claude — it's pre-wired to your key and our proxy.
Prefer your own machine?
npm install -g @anthropic-ai/claude-code, thenexport ANTHROPIC_BASE_URL="https://interview-proxy.purplerock-e0f9c506.eastus2.azurecontainerapps.io"andexport ANTHROPIC_API_KEY="<your-api-key>"before runningclaude.
Read the rules below, implement, and test.
Please use the Claude key we sent you for your AI work (via the
dev-box or by exporting ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY
as shown in step 2). The key routes through our proxy, which logs the
prompts and responses so we can grade how you used AI — not just
what you shipped. That's a deliberate part of the assessment.
You're welcome to use other AI tools alongside it (asking ChatGPT a side question, etc.), but the Claude work should go through the key we issued. If you opt out entirely, we can still grade your code, but we'll have no visibility into your process — which means most of the weight will fall on later interview stages where you'll have to walk through your decisions live.
git pushto your own (private) repo.- Share the repo with
@liammaieron GitHub — Settings → Collaborators → Add people →liammaier. We need read access to grade your submission. - Reply to your onboarding message with the repo URL.
Request body:
{
"items": [
{ "id": "item-1", "category": "clothing", "price": 95.00, "quantity": 2 },
{ "id": "item-2", "category": "electronics", "price": 499.99, "quantity": 1 }
],
"shipping": { "cost": 12.99 },
"destination": { "state": "NY", "zip": "10001" },
"seller_nexus_states": ["NY", "CA", "TX"],
"exemption_certificate": null
}Response body:
{
"items": [
{
"id": "item-1",
"taxable_amount": 0.00,
"tax": 0.00,
"exempt": true,
"exempt_reason": "Clothing under $110 threshold in NY",
"jurisdiction_breakdown": []
},
{
"id": "item-2",
"taxable_amount": 499.99,
"tax": 44.37,
"exempt": false,
"exempt_reason": null,
"jurisdiction_breakdown": [
{ "jurisdiction": "NY State", "rate": 0.04, "tax": 20.00 },
{ "jurisdiction": "New York County", "rate": 0.045, "tax": 22.50 },
{ "jurisdiction": "MCTD District", "rate": 0.00375, "tax": 1.87 }
]
}
],
"shipping": {
"taxable_amount": 12.99,
"tax": 1.15,
"jurisdiction_breakdown": [
{ "jurisdiction": "NY State", "rate": 0.04, "tax": 0.52 },
{ "jurisdiction": "New York County", "rate": 0.045, "tax": 0.58 },
{ "jurisdiction": "MCTD District", "rate": 0.00375, "tax": 0.05 }
]
},
"total_tax": 45.52,
"total_taxable": 512.98,
"total_exempt": 190.00
}Return appropriate HTTP error codes for malformed or invalid requests.
A seller only collects tax in states where they have nexus. The
seller_nexus_states field lists those. If the seller does not have nexus
in the destination state, the entire order is tax-free (no tax on items or
shipping). If the field is empty or missing, treat the seller as having
nexus everywhere.
Rates are in data/rates.json. Each ZIP maps to a state rate plus county,
city, and district rates. Each jurisdiction's tax is calculated separately
(not as a single combined rate), then the per-jurisdiction values are summed
for the line-item total.
For this project, all rate lookups use the destination ZIP. (Origin-based and modified-origin rules exist in real life, but they are out of scope here.)
Whether a product is taxable depends on its category and the destination state.
See data/taxability.json. Most rules are simple booleans.
The notable exception: clothing in NY is conditionally exempt — items priced below $110 per unit are exempt, items at $110 or above are taxable. The threshold is per-unit, not per-line-item.
Shipping is taxable when the order contains taxable items. If every item in
the order is exempt, shipping is also exempt. See shipping_rules in the
data file.
If exemption_certificate is { "type": "resale" }, all sales tax is
removed — items and shipping all become exempt regardless of category.
Tax must be rounded per jurisdiction per line item. Calculate
line_value * jurisdiction_rate, round to the nearest cent, then sum.
Floating-point arithmetic introduces subtle errors in financial math —
consider whether your language's default number type is appropriate for
currency.
Return HTTP 400 for invalid requests: unknown product categories, unknown ZIP codes, negative prices, zero quantities, empty item lists, missing required fields.
- A working API server with
POST /calculate-tax - Your code pushed to your private copy of this repo
- A short
NOTES.mddescribing assumptions, decisions, and anything you chose to skip given the time limit. NOTES.md matters — it's how you show your reasoning when output is incomplete.
Language/framework: Your choice. We'll test by hitting your API over HTTP.
- Read the data files before coding — they reveal more than the README alone
- Get the basics solid before reaching for edge cases
- If you don't have time for something, document it in
NOTES.mdrather than ship something half-broken - Use AI heavily — but route your Claude work through the key we issued (see the Build section). The proxy log is how we evaluate your process, not just your output.