Onboarding

Your first week at ZephyrCart, in checklist form. Your buddy and your manager are responsible for unblocking you when something here is wrong.

Day 1

  •  Laptop arrived. (If not — ping People Ops on #peopleops.)
  •  SSO works for Google Workspace, Slack, GitHub, Linear.
  •  1:1 booked with your manager.
  •  You've been added to your team's Slack channel and the company-wide #announcements.

Day 2–3

  •  You've read the team handbook end to end. It's short.
  •  You've read the code of conduct. Signed acknowledgment goes to People Ops.
  •  You've cloned the zephyrcart/platform monorepo and run the local stack against a test tenant. Your buddy can pair on this if the bootstrap script falls over.

Week 1

  •  You've shipped one small change to production. "Small" can be a typo fix; the point is to walk through the deploy pipeline once with someone watching.
  •  You've attended one all-hands and one of your team's weekly syncs.
  •  You've met the people listed in your who-to-know.md (your buddy will send this).

When you get stuck

The single best move is to post the question in your team channel before slacking individuals directly. We bias toward asynchronous, searchable answers — see the communication norms in the handbook.


Team Handbook

How we work at ZephyrCart. If something here disagrees with what your manager told you, your manager is right and this page is out of date — please open a PR.

Who we are

A 64-person company headquartered in Copenhagen, with offices in Berlin and Lisbon and remote teammates in Singapore, Toronto, and Cape Town. The org tree lives in BambooHR; the source of truth for who's on what team is the #team-* Slack channels and the squad map below.

Squads

  • Storefront API — owns /v1/carts, /v1/checkouts, /v1/orders. Lead: Ali.
  • Catalog — owns /v1/products, /v1/collections, search. Lead: Ali.
  • Tax & Money — owns calculation engines, currency, refunds. Lead: Ali.
  • Platform — owns the build, deploy, observability, the dev portal. Lead: Ali.
  • Growth Eng — owns the marketing site, signup, billing portal. Lead: Ali.

Communication

We bias hard toward written, asynchronous, searchable. Three rules of thumb:

  1. If more than two people care about it, put it in a channel, not a DM.
  2. If you'd say it in a meeting, write it down before the meeting.
  3. If a thread is more than ten replies long, write a doc.

Meetings are the most expensive way to make a decision. Defaults: cameras off, agenda required, notes shared within 24 hours.

How we ship

  • Trunk-based development on main. Feature flags for anything user-visible.
  • Code review by one engineer on the owning squad; design review by an EM for changes that touch the API surface or the data model.
  • Production deploys are continuous and automated. Rollbacks are one Slack command.

Calendars and time off

The platform team owns the on-call rotation; everyone else's calendar is their own. Time off goes in BambooHR a minimum of one week in advance (more if it's longer than a week). Public holidays follow your home country's calendar.


Stripe Tax integration partnership

The integration that's been in technical preview since March is now generally available, and Stripe added us to their app gallery this morning. Marketing post lands tomorrow; this is the internal one.

What ships

A per-tenant toggle to delegate US sales-tax (and a few other non-EU jurisdictions) calculation to Stripe Tax instead of our internal engine. Customer-visible benefit: nexus and economic threshold tracking that we never planned to build ourselves.

Public documentation: Stripe integration.

Commercial

This is a referral arrangement, not a revenue share. The contract sits with Ali in #legal-archive if you need to see it.

What changes for internal teams

  • Support: the runbook for "why is the tax wrong?" now branches on tax_calculation_source. Updated in the support wiki.
  • Tax & Money: the team owns the integration. Stripe Tax outages do not page us; they fall back to internal calculation.
  • Sales: new talking point for the US mid-market — see the updated battle card.

Drinks Thursday in Lisbon. Talk to Ali if you can fly down.


All-hands recap — May 15

For anyone who couldn't make it live. Recording in #allhands for the next 30 days.

Q2 numbers

Two numbers from the founder slide that aren't on the dashboard yet:

  • ARR closed April at €12.1M, up 47.2 % year-over-year (the slide had €11.8M — corrected post-meeting).
  • Net dollar retention sits at 118 %, lifted by the Stripe Tax handoff feature finally landing enterprise upsells we'd lost in 2025.

OKR check-in

Of the 14 company OKRs for Q2, 4 are green, 7 are amber, 3 are red. The red ones:

  1. "Cut catalog import latency by 50 %" — still at 18 % improvement. The catalog team revised the plan; new ETA is end of June.
  2. "Launch Brazil region" — slipped to Q3 because of regulatory complexity around Pix.
  3. "Ship the new dashboard's customer-merge UI" — held back behind the data-handling policy review (see Data handling).

Amber is fine. Red is something we'll talk about in the next manager forum.

Q&A highlights

  • On office time: the working group lands a recommendation by June 15. The honest answer remains "we trust your judgement."
  • On the hiring freeze: still on for non-platform roles through Q2. Reopens in July if Q2 closes on plan.
  • On the all-hands cadence: moving to monthly (last Thursday) instead of every three weeks.

Slides are pinned in #allhands.


ap-singapore is now GA

The Singapore region exited beta on Monday. SLA, latency targets, and on-call coverage are now production-grade. The full list of tenants that moved is in #launch-apac.

What this means for you

  • Storefront API: route customers to ap-singapore if they're east of the Bay of Bengal. p50 from Sydney drops from 220 ms (to Frankfurt) to 75 ms (to Singapore).
  • Tax & Money: the Singapore region inherits the existing tax zones; nothing else to do for GST.
  • Platform: on-call shadow rotation starts next sprint. Sign up at go/oncall-apac.

What it does not mean

  • The catalog is still global; switching a tenant's region requires a real migration.
  • No automated cross-region failover. See the deployment topology.

Champagne in the Copenhagen kitchen Friday 16:00 if you're around.


Platform update — May rollups

Three things shipped this month that are worth knowing about even if you don't work on the platform team.

Build pipeline cut to under 6 minutes

The platform team rewrote the test sharding for zephyrcart/platform. Cold builds now finish in 5m40s p50 instead of 12m. The savings come from running integration tests in parallel against per-shard ephemeral PostgreSQL containers. Detail: PR #14228.

Frankfurt region capacity doubled

Customer growth in DACH pushed Frankfurt utilisation above 60 % at peak. We doubled the API tier and the PostgreSQL read replicas. No customer-visible change; latency p99 dropped from 92 ms to 71 ms.

Lucene index for product search rebuilt

The new schema indexes metadata.* fields if their key starts with searchable_. Catalog teams, you may now ship the metadata-as-facets work that was blocked on this.

— Ali


Friday-afternoon shipping window — opt-out next sprint

The "no deploys after 14:00 Friday" rule has been in place for two years. It catches a real class of weekend incidents, but it also burns Friday afternoons for teams that ship low-risk PRs.

Starting next sprint, the policy becomes opt-in per service:

  • Default behaviour is unchanged — the deploy pipeline blocks production deploys after 14:00 local.
  • A service can opt out by adding friday_afternoon_deploys: allowed to its service.yaml.
  • Opting out requires the owning EM's signoff (the file enforces an EM-only CODEOWNER for that key).

Two reasons we're loosening it now: trunk-based CI catches regressions earlier than it did in 2024, and the on-call rotation has full APAC coverage so a Friday deploy isn't a EU-only burden.

If your team wants to opt out, the PR template is in runbooks/opt-out-friday.md.


Code of conduct

What we expect of each other at ZephyrCart. Short, on purpose.

Be kind, candid, and specific

Disagree with the work, not the person. "This API name is confusing because…" is the shape; "you made a confusing API" is not. Candid feedback is a gift; vagueness wrapped in politeness is not.

Assume good intent. Verify if you can't.

If a colleague did something that surprised you, ask before reacting. The asynchronous, written default helps here — re-read the slack message before responding.

Respect boundaries

People's time, attention, and on-call windows are theirs. Don't ping outside business hours unless it's actually urgent. "Actually urgent" means "customers are affected and you've already paged on-call."

No harassment, ever

Harassment in any form — sexual, racial, religious, age-based, anything — is grounds for termination. There is no grey area. If you witness it or experience it, contact your manager, People Ops, or one of the two designated trust contacts (listed in BambooHR). Reports are confidential.

Conflicts of interest

If you have a financial relationship with a customer, a competitor, or a vendor, disclose it in writing to People Ops. The default response is "thank you for telling us, here's how to recuse yourself." It is almost never "you have to give it up."

How this is enforced

The first response is always a private conversation. Repeated or egregious breaches escalate to formal warnings and, ultimately, termination. The trust contacts maintain an anonymised record of patterns; they share it with the founders quarterly.

You acknowledge this code as part of Onboarding. Signed acknowledgments live in BambooHR.


Data handling

How customer data moves, who can touch it, and what we never do with it.

Classification

Every piece of data we handle falls into one of four buckets:

ClassExampleStorageAccess
PublicMarketing copy, API specAnywhereEveryone
InternalThis intranet, OKRsSSO-gated toolsAll employees
ConfidentialCustomer email, order data, payment metadataTenant region onlyRole-gated, audited
RestrictedPAN, CVV (not stored), employee compensationVault, KMS-encryptedTwo-person rule, logged

What stays in the tenant's region

A tenant's Confidential data does not leave its home region. There is no cross-region read replica of customer PII. Telemetry that flows centrally is de-identified at source and listed explicitly:

  • Request counts, latencies, error rates, by route + status
  • Aggregate billing totals, by tenant id (not by customer id)
  • Webhook delivery success rates, by endpoint host (not by URL path)

What does not flow centrally: customer emails, names, addresses, order line items, payment tokens, IP addresses.

Access requests

Customer-data access for support requires:

  1. An open support ticket from the customer's verified contact.
  2. A manager approval click in the access tool. Approvals expire after 24 hours.
  3. The query runs against the tenant's region. The audit log captures who, when, what, why.
Raw HTML passes through the renderer — this paragraph demonstrates that.

What we never do

  • Sell, lease, share, or otherwise transmit customer data to third parties for marketing.
  • Train models on identifiable customer data. (Aggregate, anonymised telemetry is fair game for internal product analytics.)
  • Store full card numbers (PAN). Stripe holds the cards; we hold a tokenised handle.

In a breach

The first action is the incident response runbook. The second is the legal hold and notification — Legal owns this and will direct.

Audits

We run a third-party penetration test annually (currently with Trail of Bits) and an internal red-team exercise quarterly. Findings land in the security channel within 30 days of the engagement closing.


Incident response

Read this before your first on-call shift. The runbook lives in the platform repo at runbooks/incident.md; this page is the policy and the mental model.

You will not be punished for declaring an incident that turned out not to be one. The cost of false positives is small; the cost of false negatives is missed pages, frustrated customers, and unanswered status pages.

Severity ladder

SeverityExamplesWho pages, who joins
SEV-1Region down. Payment provider down across multiple tenants. Data breach.All platform on-call + EM on-call + founder on-call
SEV-2One service unhealthy, customer-visible degradation, no data loss.Service owner + platform on-call
SEV-3Internal degradation, no customer impact.Service owner only

Start the incident

zephyr incident open --sev 2 --summary "Checkout p99 elevated in eu-frankfurt"

The CLI creates the Slack channel, the status page entry, and the Linear ticket. It pages the right rotation. It pins the runbook in the channel.

During the incident

Three roles, even for a small SEV-2:

  • Incident commander (IC) — owns the timeline, the decisions, the channel. Not necessarily the most technical person; the calmest one.
  • Comms — owns the status page, the customer email, the after-action announcement. Often the EM.
  • Investigator(s) — the people in the code. Usually two; one drives, one rubber-ducks.

Status updates land in the channel and on the status page every 15 minutes minimum, even if the update is "still investigating." Silence is worse than bad news.

After the incident

A blameless post-mortem within 72 hours, drafted by the IC, reviewed by the team. The template lives at runbooks/post-mortem-template.md. It goes in the platform repo, not on the intranet — post-mortems are searchable engineering knowledge, not internal politics.

The post-mortem closes with one or more action items, each owned by a named person and ticketed in Linear with a "post-mortem" label. If none ever ship, escalate that pattern.

Customer communication

For SEV-1 and SEV-2 we send a customer email within 24 hours of resolution. The template is in comms/incident-email-template.md. Tone: factual, brief, no marketing language. Apologise once.

Out of hours

The on-call rotation crosses time zones so there is always someone awake. If the page rings and nobody answers within 5 minutes, the escalation flow pages the next person in the rotation. If the second person doesn't answer within 5 more, the EM on-call is paged. If they don't answer, the founder on-call is paged. The chain is unbroken for a reason.