Home

Emisys

Year
2026
Type
Work project · Tres Astronautas
Status
In development
Stack
  • Go
  • PostgreSQL
  • Next.js
  • React
  • Tailwind CSS
  • shadcn/ui
  • gRPC and OpenAPI
  • Turborepo and pnpm
  • PWA

Summary

Emisys lets employee funds, the savings and credit cooperatives many companies offer their staff, give their members a credit card without building their own issuing, authorization, reconciliation and control systems.

It acts as the record and the orchestrator of the card program. It onboards each fund, configures products and rules, manages members and the full life of each card, adds context to every purchase, records transactions and their corrections, and controls the money each fund must put up front. What it does not do matters as much: Emisys never moves money, never makes the credit decision and never sees a card's PIN. The fund keeps its authority, and each third party keeps its own job.

The product is made of four web apps on one platform:

  • A back-office for the team that operates the program.
  • A fund portal for each fund's administrators and users, under the fund's own brand.
  • A member app on the phone, also under the fund's brand, the only channel members use.
  • A governance portal where reviewers approve settlement and collateral.

Emisys is one half of a two-part product. Mottika, documented as its own project, is the transactional switch behind it.

I work on Emisys at Tres Astronautas for a client, as a Senior Software Engineer, building most of the API and the web apps. The project is confidential and still in development, so this case study shows flows and diagrams instead of screens.

26
capabilities
115
features
4
web apps: back-office, fund portal, member app and governance portal

Context & role

  • Client: A financial-services company. The name is kept private.
  • Company: Tres Astronautas
  • Product: A back-office, a fund portal, a member app and a governance portal on one platform
  • Languages: Spanish and English
  • My role: Senior Software Engineer
  • What I built: Most of the API and the web apps
  • Status: In development

Problem

A card program for an employee fund depends on many parties at once: the processor that issues and authorizes cards, the fund's own systems that make the credit decision, a reconciliation platform, an accounting core and the members themselves. Without one platform in the middle, every step between them becomes a hand-off by email, file or phone, and each hand-off is a place where something gets lost, typed twice or left without a trace.

The scope of the platform shows where that hurts:

  • Fund administrators need to register members, request and assign cards and set their own rules, without depending on someone else for each step.
  • Members need one place to see their card, activate it, set the PIN and block it when something goes wrong.
  • The operating team needs to onboard funds, define products and watch every integration from one place.
  • Reconciliation analysts need each period's figures to be explainable later, with approvals that leave evidence.

The fund administrator carries most of that weight. It is the profile that crosses the most flows, from onboarding its own fund to the card in a member's hand, and most of the fund portal is designed around making each of those steps clear and safe to repeat.

Users & context of use

Five profiles work with Emisys. The external parties, such as the card processor or the fund's authorizer, are modeled as integration points and not as people.

Fund administrator

Registers members, requests and assigns cards, sets the fund's transaction rules and follows claims, for their own fund only.

Fund portal

Member

A member of the fund who sees their card, balance and movements, activates the card, sets the PIN and blocks it.

Member app, phone first

Platform operator

Onboards funds, configures products and rules, forms card batches and watches every integration.

Back-office

Reconciliation analyst

Reconciles each period, manages differences and controls what goes to accounting.

Back-office

Governance reviewer

Reviews settlement and collateral with their evidence and approves them through a configurable flow.

Governance portal

The moments that shaped the design

  • The member activating a card is on the phone, often for the first time. The app had to be one task per screen, and the PIN had to go straight to the processor without passing through Emisys.
  • The fund administrator registering members in bulk uploads a file and needs a result for each row, not a single "failed" for the whole batch.
  • The operator facing a slow third party needs to see what is pending and act only through the recovery actions that are allowed.
  • The analyst closing a period needs every figure to be traceable, with a second approval where money is involved.

These moments come from the functional backlog and the UX blueprint of the project, and they worked as a check for every flow.

Goals, success metrics & constraints

Goals

  1. Shorten the time from registering a member to an active card, with fewer requests that need rework.
  2. Keep operations flowing when a third party fails, with safe retries and recovery.
  3. Keep financial control from end to end, and resolve differences inside each period.
  4. Connect every third party through stable, versioned contracts.
  5. Leave a trace of every relevant operation.

How success would be measured

The targets for each goal were left open on purpose, until a baseline can be measured after go-live. These are the metrics I would propose, each tied to one of them:

GoalMetricWhat it tells us
Faster cardsDays from member registration to an active card, and share of requests that need reworkWhether the flow from request to activation actually got shorter.
ContinuityOperations left indeterminate, and time to resolve themHow often a third party slows the program, and how fast it recovers.
Financial controlDifferences per period, and share resolved before the closeWhether reconciliation happens inside the cycle.
Governed integrationsContract changes per release, and messages rejected at the edgeHow stable the connections with third parties are.
TraceabilityShare of critical operations that can be rebuilt end to endWhether an audit can follow any transaction.

Constraints

  • Many parties, few of them ready. The processor, the fund's authorizer and other integrations were not available during the first chunks, so the platform was built against contracts and stand-ins that answer "pending".
  • Clear boundaries. Emisys never moves money, never makes the credit decision and never sees a PIN, and every screen had to respect that.
  • Isolation between funds. No fund can see another's data, and fund users never choose a fund: the context comes from who they are.
  • Two languages and two themes from the start, with the fund portal and the member app under each fund's brand.
  • Open business decisions, such as how a fund confirms its integration channel or whether a bulk upload is all-or-nothing, were still being made while the build moved forward.

Process & prioritization

From problem to scope

The work started from an impact map that tied the business goals to the people and the systems involved. That map became a functional backlog of 26 capabilities and 115 features, each one written as a user story with its trigger, preconditions, flow, business rules and acceptance criteria.

A single catalog of identifiers runs through the backlog, the architecture, the domain model and the UX blueprint, so a feature can be followed from the story to the contract, the screen and the test.

What the platform covers

AreaWhat it enables
Identity and accessUsers, custom roles with granular permissions and a scope per fund, decided by one authority.
Fund onboardingA wizard, an invitation for the first administrator, a checklist with evidence and activation.
Products and rulesA versioned product catalog with approval, platform rules and each fund's own transaction rules.
MembersRegistration one by one, in bulk or through an integration, with changes that cascade to cards.
Card supply and lifecycleBatches, stock per fund, assignment, delivery, activation, PIN, blocks, cancellation and reissue.
Authorizations and transactionsContext for each purchase, and transactions with their voids, refunds and reversals kept apart.
Statements and reportsThe member's view of the card, and reports and dashboards for each fund.
Money controlPrefunding, collateral, reconciliation, invoicing, accounting entries and cashback.
ServiceClaims, chargebacks, notifications and a link with the help desk.

Built in chunks

Delivery is split into a cross-cutting chunk and five chunks of business, each one reviewed before the next starts. The contracts for each chunk are written before its code.

  • Built: identity and access, fund onboarding, products, rules and members, with the four apps, sign-in, password recovery, two languages, two themes and installable apps.
  • Next: card batches and lifecycle, authorizations and transactions, statements, and then reconciliation and invoicing.
  • Waiting on others: the real integrations with the switch, the fund's authorizer and the processor. Until they exist, stand-ins answer "pending" instead of inventing a result.

Design decisions

The decisions that most shaped the experience, made by the team and designed by the product designer of the project, part of the Tres Astronautas team. The project is confidential, so each one is shown as a flow or a diagram instead of a screen.

A fund goes live through a checklist

Problem. A fund that starts operating with missing documents, an unpaid fee or an unclear integration creates problems that are hard to undo later.

Decision. The operator creates the fund with exactly one first administrator, who then builds the team and uploads evidence. The operator validates each item, and the fund goes live only when every mandatory item is met.

Trade-off. Activation takes longer than a single "create and go" form.

In the product. Each fund has two separate states, one for onboarding and one for operation, and the first activation moves both in a single step. A repeated request never creates a second fund or a second invitation.

Onboarding a fundFrom the operator's wizard to a fund with its rules in force, across two profiles.

Each step of a card has one owner

Problem. A physical card passes through the fund, the operator, the processor and the member. If any of them can skip a step, nobody knows where a card really is.

Decision. The fund requests, the operator forms the batch, the processor makes it and the fund confirms receipt. Stock grows only on that confirmation, and only the member can activate the card.

Trade-off. More confirmations, and a card that is delivered but not yet usable until the member acts.

In the product. Assigning a card takes exactly one from stock, even if the request is repeated, and an answer that never arrives is shown as indeterminate, never as done.

From a request to an active cardFour profiles take part, and each step has one owner.

Every block remembers who set it

Problem. A card can be blocked by the fund, by the member, for fraud or because the member left the company. If any block could lift any other, one action could undo a decision someone else made for a good reason.

Decision. Each restriction keeps its origin. A fund lifts only the blocks it set, and the card goes back to active only when the last one is lifted.

Trade-off. A more complex model behind a simple "blocked" label.

In the product. When a fund reports a change in a member, the cards follow a fixed policy: suspended blocks them, withdrawn cancels them, and reactivated lifts only the block that status caused.

A card's life cycleThe states of a card, and the rules that protect each transition.

"Indeterminate" is a state, not an error

Problem. In a financial product, showing "approved" or "rejected" when the real answer never arrived is worse than showing nothing.

Decision. Every operation has an explicit state, and a timeout is recorded as indeterminate until the real result is known. The interface never shows data it does not have.

Trade-off. Some screens say "pending confirmation" or "unavailable" for a while, and the platform needs queues to resolve those cases later.

In the product. The same rule runs through the whole platform: accepted but not yet confirmed, indeterminate, unconfirmed and pending intervention are states people can see, with the actions each one allows.

One purchase, four partiesWho decides what in an authorization, and what happens on a timeout.

The fund's brand, not the provider's

Problem. The card program belongs to the fund. Members should feel they are dealing with their fund, not with a platform they never chose.

Decision. The fund portal and the member app are white-label: each fund's logo and brand color load after sign-in. The back-office keeps its own brand.

Trade-off. Every component has to work with colors it does not know in advance.

In the product. The brand color derives a primary, a soft tint and a darker text shade, and the design system checks the contrast of each pair in both themes.

The fund comes from the session

Problem. A fund user who has to choose "their" fund can choose the wrong one, and a list of funds already tells them more than they should know.

Decision. Fund users never see a fund selector. The fund comes from who is signed in, and every query is scoped on the server.

Trade-off. Every screen depends on the server resolving the context correctly, so isolation is tested on its own.

In the product. Another fund's data looks as if it does not exist, and the active context is always visible in the header.

Design system

The four apps share one design system, created in Figma by the product designer assigned to the project at Tres Astronautas and documented as a manual of foundations, components and page templates. A person who moves from the back-office to a fund's portal recognizes every piece, even under another brand.

Foundations

  • Two typefaces with clear roles. Space Grotesk, a geometric sans serif, for titles. Satoshi for the interface, forms, tables and data.
  • A brand with a dark base. A deep navy for structure, magenta for the main action, orange as a secondary action and yellow as an accent, with a gradient reserved for sign-in.
  • A dark theme that comes for free. Each color ramp is simply reversed for the dark theme, so every semantic token is declared once and works in both.
  • Status colors with fixed meanings. Success, error, warning and info always mean the same thing, and every pair was checked for contrast in both themes.
  • A closed radius scale and a dense type scale. The default sizes of the framework were removed, so only the system's values can be used.
FoundationsTypography, color, status colors, radius and spacing shared by the four apps.

Built into the apps

I brought the design system into code as a shared component package used by the four apps. The colors, typography and radius became theme tokens with a light and a dark version, and the brand tokens can be replaced at runtime, which is how each fund's color reaches its portal and its member app. New primitives can only be added to the package, never inside an app, so the four apps stay consistent as they grow.

From design to code

As a Senior Software Engineer, my work is turning the design and the architecture into a working product. This section explains how the platform is organized, in terms of what each part does.

Architecture

The four apps talk to the platform through a single gateway, always from their own server side. Behind it, one modular application holds the business: funds, products, rules, members, cards, audit and integrations. A separate service answers every "can this person do this, on this fund?" and denies by default.

Work that does not need an immediate answer goes through queues, kept in order per card, per fund or per period where order matters. Every third party, Mottika included, sits behind a versioned contract, and Emisys never reads another system's database.

ArchitectureThe four apps, the core with its authorization service, and the third parties behind contracts.

States people can trust

The domain model describes every entity with a life cycle, its states and who can move each one. A product, for example, goes from editable to validated, authorized and published, and can be suspended or retired, with existing cards following the policy chosen.

These states are what the screens show. Because each transition is explicit, a button appears only when the action is allowed, and an operation that is still waiting for a third party says so.

Life cyclesThe states of a fund, a product and a card request.

Contracts first

Each capability has its own OpenAPI contract, written and reviewed before its code and checked automatically. A test fails when a contract operation has no implementation, and the API documentation and the request collections are generated from the contracts, so they never go out of date.

Delivery

Every change runs automated quality and security checks before it is merged, and each service and app has its own deployment workflow. Testing goes deepest where the risk is highest: authorizations and their corrections, isolation between funds, duplicated accounting and the PIN never being stored.

Working with AI agents

The work is spec-driven, and the repository carries maps and rules for AI coding agents, with session boards, claims on shared paths and reviewer checklists, so several sessions can work in parallel without colliding. People keep control of every commit.

Technical decisions

DecisionWhyTrade-off
Third parties as integration pointsEmisys stays independent of each third party's internals.Every integration needs a contract and an adapter.
One contract per capability, before codeTeams and parties can work in parallel against stable interfaces.Slower first delivery.
A separate authorization servicePermissions are decided in one place, the same way for every app.One more call for each protected action.
Idempotent critical operationsA retry never duplicates a fund, a card or a payment.Every write needs an operation key.
Indeterminate as its own stateA timeout is never shown as approved or rejected.Queues and manual states to resolve it later.
Four apps by audienceEach audience gets a focused experience and its own release.Shared pieces must live in the design system.

Results

The foundation of the platform is built: identity and access, fund onboarding, products, rules and members, with the four apps, sign-in, password recovery, two languages, light and dark themes and installable apps. Funds can be onboarded and governed end to end, each isolated from the others.

The project is still in development, so there are no usage figures yet. The next chunks bring the card lifecycle, transactions, statements, reconciliation and invoicing, and the real integrations with the switch, the fund's authorizer and the processor depend on other parties.

Learnings & next steps

Next steps

  • Connect the real switch, authorizer and processor in place of the stand-ins.
  • Build the card lifecycle and transactions on the contracts that are already written.
  • Take the first measurements after go-live as the baseline for the targets.

What I learned

  • An honest state is a design decision. Showing "pending confirmation" instead of a guess felt like a worse experience at first. In a financial product it is the opposite: people trust the screens because the screens never pretend.
  • Contracts let you build before the other side exists. With the processor and the authorizer not yet available, the contracts and their stand-ins let the apps and the flows move forward, and they made the remaining work easy to see.
  • White-label is a constraint on every component. Designing for colors you do not know in advance pushed the design system to rely on tokens everywhere, and it made the dark theme almost free.