⚡ ⚡ Learn how Investing.com uses Jitsu to move data faster

Fintech analytics on event data you own

A fintech analytics question such as "how many September signups passed KYC and funded within a week" needs two things at once: the backend's KYC and deposit events in one warehouse, and no card numbers, account numbers or identity documents in any tool those events reach. Jitsu, an open-source event pipeline under the MIT license, takes events from the website, the apps and the backend, runs JavaScript functions you write on each connection, and sends the result to a warehouse and the tools you choose.

TL;DR

  • The backend sends KYC decisions and settled deposits over an HTTP API with a server-to-server write key.
  • Jitsu's JavaScript library uses the same identify, track and page calls as Segment's analytics.js.
  • Functions attach per connection: the warehouse connection keeps every event, and the ads connection gets page views and two conversions.
  • Jitsu bills active events: 200,000 a month on Free, $99 a month for 2M on Business.
  • Self-hosted Jitsu runs on your Kubernetes cluster; Jitsu Cloud is hosted in the United States.

Jitsu fits if:

  • Raw events belong in your own warehouse: ClickHouse, BigQuery, Snowflake, Redshift or Postgres.
  • Conversions for Meta Ads and Google Ads have to pass through code you control first.
  • Policy requires the pipeline to run inside your infrastructure. Jitsu self-hosts on Kubernetes.

Look elsewhere if:

  • A mobile attribution tool has to receive your events. None is a live destination today (destinations directory).
  • Your mobile team wants a Jitsu-built SDK. There is none: apps use Segment's open-source iOS, Android and React Native libraries, pointed at your Jitsu domain.

Fintech events to send through Jitsu

Financial facts come from the backend, because only the backend knows a KYC decision or a settled deposit.

EventSent fromWhy it matters
pageBrowserUTM parameters and ad click IDs
identifyBrowser, app, backendLinks the anonymous ID to your internal user ID
Account CreatedBackendFunnel start; first ad conversion
KYC SubmittedBrowser, app or backendWho reached identity verification
KYC Approved, KYC RejectedBackend, from the KYC webhookThe decision only the backend sees
Bank Account LinkedBackendThe step before funding
Deposit CompletedBackendSettled funds
Account FundedBackend, once per userThe conversion ad platforms optimize on
Order FilledBackendFirst trade
Card Transaction AuthorizedBackendCard usage, no card data

The full plan has 35 events with source, properties, types and PII handling: download the fintech tracking plan (CSV), not gated.

How the pipeline is laid out

Fintech event pipeline on Jitsu: sources, one chain of functions per connection, live destinations

The website runs Jitsu's JavaScript library, and apps use Segment's open-source libraries pointed at the Jitsu domain (iOS, Android, React Native). Your backend posts to /api/s/{type} with a server-to-server write key and Strict Mode on. It passes context.ip and context.userAgent itself, because Jitsu does not read them from a server's request (server-to-server docs).

Each destination sits on its own connection with its own chain of functions, and functions do not run on device destinations or on connector data.

What fintech companies run on Jitsu

The public story: Investing.com

Investing.com publishes financial markets data, news and tools. As published, it sends website, Android and iPhone events through Jitsu to Google BigQuery, Google Ads and Mixpanel at 5 billion events a month. It moved enrichment out of client code into Jitsu functions that call external APIs and cache the responses in Jitsu's key-value store.

What others in the industry do

These are industry examples. None of these companies is presented here as a Jitsu customer.

Example implementation (illustrative): a brokerage app

Illustrative example: not a Jitsu customer.

A brokerage app sends every event to ClickHouse, product events to Amplitude and two conversions to Meta Ads and Google Ads, each on its own connection. Some free text users type, in support messages or transfer references, contains card numbers. One function runs first on every connection: it removes regulated identifiers by key name at any depth, and drops any event with a value matching a 13 to 19 digit card pattern that passes the Luhn check, logging the field name to Live Events, never the value.

// Removed at any depth. Keys are compared lowercased, with "_", "-" and spaces stripped.
// Add your own field names to this list.
const FORBIDDEN_KEYS = new Set([
  "cardnumber", "ccnumber", "creditcardnumber", "pan", "cvv", "cvv2", "cvc", "securitycode",
  "cardexpiry", "expirydate", "expdate", "iban", "accountnumber", "accountno", "routingnumber", "sortcode",
  "ssn", "socialsecuritynumber", "nationalid", "taxid", "passportnumber", "documentnumber",
  "dateofbirth", "dob", "birthdate", "password", "otp", "pin",
]);

// Keys whose values are IDs and are not scanned: id, deposit_id, messageId, transactionID.
const ID_KEY = /(^|[_\-\s])(id|Id|ID)$|[a-z0-9](Id|ID)$/;

// A run of 13 to 19 digits starting with 2-6, with optional single spaces or dashes between digits.
// Every digit group is tried as a start, so a date or an amount in front cannot hide a card number.
const CARD_START = /(?<!\d)(?<!\+)(?=([2-6](?:[ -]?\d){12,18}))/g;

function passesLuhn(digits) {
  let sum = 0;
  for (let i = 0; i < digits.length; i++) {
    let d = Number(digits[digits.length - 1 - i]);
    if (i % 2 === 1) d = d * 2 > 9 ? d * 2 - 9 : d * 2;
    sum += d;
  }
  return sum % 10 === 0;
}

function hasCardNumber(value) {
  for (const match of value.matchAll(CARD_START)) {
    const run = match[1];
    const after = value[match.index + run.length];
    let digits = "";
    for (let i = 0; i < run.length; i++) {
      if (!/\d/.test(run[i])) continue;
      digits += run[i];
      const next = i + 1 < run.length ? run[i + 1] : after;
      const groupEnds = next === undefined || !/\d/.test(next);
      if (groupEnds && digits.length >= 13 && passesLuhn(digits)) return true;
    }
  }
  return false;
}

// Copies the value without forbidden keys and records where a card number appears.
function scrub(value, path, hits) {
  if (Array.isArray(value)) return value.map((v, i) => scrub(v, `${path}[${i}]`, hits));
  if (value !== null && typeof value === "object") {
    const out = {};
    for (const [key, v] of Object.entries(value)) {
      if (FORBIDDEN_KEYS.has(key.toLowerCase().replace(/[_\-\s]/g, ""))) continue;
      out[key] = ID_KEY.test(key) ? v : scrub(v, path ? `${path}.${key}` : key, hits);
    }
    return out;
  }
  if ((typeof value === "string" || typeof value === "number") && hasCardNumber(String(value))) {
    hits.push(path);
  }
  return value;
}

export default async function piiGuardrail(event, { log }) {
  if (!event || typeof event.type !== "string") {
    log.warn("pii-guardrail: dropped an event with no type");
    return "drop";
  }
  const hits = [];
  const clean = scrub(event, "", hits);
  if (hits.length > 0) {
    log.warn(`pii-guardrail: dropped ${event.event ?? event.type}, card number pattern at ${hits.join(", ")}`);
    return "drop";
  }
  return clean;
}
  • It is a second line of defense. Functions run after Jitsu receives the event, and Jitsu Cloud's event backups keep events as received, so keep card numbers out of events at the source and tracking scripts off card-entry pages.
  • A dropped event is not billed unless the function called fetch before dropping it, and this one never calls fetch (advanced functions).
  • Any 13 to 19 digit number starting with 2 to 6 that passes the Luhn check drops the event, and 1 in 10 random numbers of that length passes it. Keep long numeric IDs out of page URLs, or put them under a key ending in id, which is not scanned.
  • Store phone numbers in E.164 with the leading +, because a 13-digit number without it can match the card pattern.

Use cases

Where Jitsu does not fit

  • Identity stitching is part of the Enterprise plan on Jitsu Cloud; self-hosted Jitsu has no plan restriction. It rewrites anonymous events in six warehouses (ClickHouse, BigQuery, PostgreSQL, Redshift, Snowflake, MySQL), up to 30 days back, and never reaches analytics tools or ad platforms.
  • Profiles from Profile Builder are an Enterprise feature of Jitsu Cloud, written to the warehouse only.
  • Hosting: Jitsu Cloud runs in the United States, and no other Cloud region is documented. Data that has to stay in a region means self-hosting. On Prem and Private Cloud are on the Enterprise plan.
  • Compliance documents: the security page covers SOC 2, with the report on request.
  • Self-hosting a feature-complete Jitsu needs Kubernetes and the Jitsu operator.
  • Destinations: Braze is live for lifecycle messaging, and no mobile attribution tool is a live destination today. Check the directory for the rest of your stack.

What it costs

Jitsu bills on active events: an event sent successfully to at least one destination counts once, however many destinations it reaches. Captured events are unlimited. A dropped event is not billed unless the function called fetch before dropping it, and a function that splits one event into several is billed per resulting event.

Active events a monthPlanPrice
Up to 200,000Free$0
2,000,000Business$99 a month
5,000,000Business$219 a month ($99 + 3 × $40)
10,000,000Business$419 a month ($99 + 8 × $40)

Identity stitching and Profile Builder are Enterprise features on Jitsu Cloud, and Enterprise pricing is custom. On Prem and Private Cloud are on the Enterprise plan. Self-hosting under the MIT license carries no Jitsu bill.

Jitsu vs Segment works the same volumes against Segment's published prices (pricing page), and the migration guide covers switching.

Questions people ask

Can card data go through Jitsu? Keep card numbers out of events at the source. Functions run after Jitsu receives an event, and Jitsu Cloud's event backups hold events as received. A guardrail function that drops events carrying a card number is a second line of defense behind the source.

Where is Jitsu Cloud hosted? In the United States. Teams whose data has to stay in a region or inside their own network self-host Jitsu under the MIT license. On Prem and Private Cloud are on the Enterprise plan.

Which destinations does identity stitching update? Warehouses only. It rewrites a visitor's earlier anonymous events in ClickHouse, BigQuery, PostgreSQL, Redshift, Snowflake or MySQL once they identify, up to 30 days back. On Jitsu Cloud it is part of the Enterprise plan; self-hosted Jitsu has no plan restriction.

Can Jitsu run alongside Segment? Yes. Add Jitsu's plugin to Segment's analytics.js, or point a Segment Webhooks destination at Jitsu, and both receive the same events while you compare them.

Get started with Jitsu

Identity stitching on Jitsu Cloud starts with a call: Book a demo. On Prem and Private Cloud are on the Enterprise plan. To send events today, Get started on the Free plan, which includes 200,000 active events a month.

Subscribe to the Jitsu newsletter
Occasional product updates when we ship something worth knowing about. No spam.