Skip to content
Facteur
FRGet started

Integrations

The channel is your test framework

On this market you do not integrate an API: you install a package into your framework. We publish four of them, and this page carries the address of each, what stays conditional, and what we will not publish.

Today

An HTTP API, and nothing to install

The four packages are optional, and that is the point: the waiting happens on the server and the extraction on reception, so a test calls the message search without installing anything. Here is a complete email verification journey, in Playwright, without a single package from us.

signup.spec.tsTypeScript
import { expect, test } from '@playwright/test';

const KEY = process.env.FACTEUR_KEY!;
const INBOX = 'j3k9x2mq';
const DOMAIN = `${INBOX}.inbox.facteur.eu`;

const awaitMessage = (sentTo: string) =>
  fetch('https://api.facteur.eu/v1/messages/search', {
    method: 'POST',
    headers: {
      authorization: `Bearer ${KEY}`,
      'content-type': 'application/json',
    },
    body: JSON.stringify({ inbox: INBOX, sentTo, wait: 30_000 }),
  }).then((response) => response.json());

test('the verification link activates the account', async ({ page }) => {
  const tag = `run-${Date.now()}`;

  // Issued before the click: it waits, and answers as
  // soon as the message lands.
  const arriving = awaitMessage(`signup+${tag}`);

  await page.goto('/signup');
  await page.getByLabel('Email').fill(`signup+${tag}@${DOMAIN}`);
  await page.getByRole('button', { name: 'Create my account' }).click();

  const { messages } = await arriving;
  const message = messages[0];

  // The status before anything derived from the body.
  expect(message.status).toBe('parsed');
  await page.goto(message.links[0].href);

  await expect(page.getByText('Account verified')).toBeVisible();
});
  • The request goes out before the click

    It waits, and answers the moment the message lands, not at the deadline. Issued after, it would be chasing a message already there.

  • Subaddressing separates the tests

    Everything after the “+” is yours. It is what lets two tests share an inbox without stealing each other’s messages.

  • status is read before any derived field

    A message is accepted before it is parsed: for a few milliseconds it has no subject, no code and no links. That is what separates “there is no code in this message” from “the code is not extracted yet”.

  • The key is read from the environment

    Never written into the test, never passed as an argument. That is the convention the plugins keep.

What we publish

A plugin before a library, and JavaScript before anything else

The order is not negotiable along the way, and it is not a matter of taste: it follows a measurement, retaken before every delivery.

DeliverableStateWhat unlocks it
A minimal Node library

Open an inbox, wait for a message, read it with what was extracted from it, purge. That is all, and it is deliberate: the product’s narrow scope holds for its public surface too. Zero dependencies: a test suite does not want a dependency tree.

Published@facteur-eu/sdkThe foundation of the plugin, and our first publicly measurable traction.
A Playwright plugin

An address derived from the test itself, so two tests running in parallel never read the same mail, and a retried test does not re-read the mail of its first attempt. The assertions are retried by Playwright’s own engine, the wait stops inside the test’s budget, and the message is attached to the report: a failure shows the mail that arrived, not just a red line.

Publishedplaywright-facteurThe largest channel on the market, and nobody is in it.
A Cypress plugin

A registered command returning a chainable, rather than a fixture. Eighty per cent of the work is shared with the one above.

Publishedcypress-facteurThe channel competitors have already proved, and eighty per cent of the work shared with the Playwright plugin.
An npm create initialiser

From an empty repository to a first passing test in one command.

Publishedcreate-facteurAn integrator’s first minute, which is the one that decides.
A Python library

The only credible second ecosystem. We are not taking it on anticipation: the trigger is a number, not an impression.

ConditionalTrigger: steady monthly use of the Playwright plugin, and a Python request arriving through issues or support.
The other ecosystems

.NET · Ruby · PHP · Java · Go · Swift · Dart

Not on the programme, and we would rather write it down. Demand there is two to three orders of magnitude below npm, and the incumbent, who maintains ten, leaves its oldest package three major generations behind its main library. Nine libraries with no measured use are nine debts, not coverage.

Not plannedNothing. A box ticked at purchase is not an integration.

Why Playwright first

The largest channel on the market is unoccupied

Read off npm, on the date shown, and replayable by anybody.

FrameworkMonthly installsCompetitor plugin
@playwright/test201,269,570None
cypress30,638,605Two

Read on npm on 17 August 2026

None of our competitors has a Playwright plugin; two have one for Cypress. Their npm keywords still carry “selenium”, “webdriver” and “nightwatch”: their findability is a generation behind actual practice.

The reservation was written before the code: nobody is there, and there might have been a reason. Playwright’s fixtures make a plugin less indispensable than with Cypress, so the checkpoint was whether a ten-line fixture in the documentation would do. It would not: the address derived from the test, the retry, the wait budget and the message attached to the report do not fit in ten lines. That is what decided the publication.

Nothing to install for a first test

A read-only key, an inbox, and the example above. The free plan asks for no card.