Synthetic Monitoring with Real Browsers.
Install the package
npm install -g @elastic/syntheticsRun the suites via CLI
npx @elastic/synthetics [options] [dir] [files] file
Use --workdir /path/to/scratch to select a writable directory for runtime cache
files, including screenshots, and temporary push bundles. The programmatic
equivalent is
run({ workdir: '/path/to/scratch' }). Relative paths resolve against the current
working directory. The default is .synthetics in the current working directory.
Synthetics creates and removes a process-specific subdirectory (<workdir>/<pid>),
leaving the work directory itself intact. For pushes, use
npx @elastic/synthetics push --workdir /path/to/scratch; bundles are created under
<workdir>/bundles and that subdirectory is removed after successful bundling.
Project discovery and configuration resolution are unaffected. The caller must
provide write access for the user running Synthetics;
Synthetics does not change ownership or permissions on existing directories.
The default journey() DSL launches a real Chromium browser via Playwright and
is the right choice when you want to verify user-facing flows.
import { journey, step, expect } from '@elastic/synthetics';
journey('homepage loads', ({ page, params }) => {
step('open', async () => {
await page.goto(params.url);
});
step('check title', async () => {
expect(await page.title()).toContain('Welcome');
});
});For API-only checks, use apiJourney(). It runs without launching a browser,
which makes it suitable for OAuth-protected endpoints, multi-step API flows,
and high-frequency lightweight monitoring.
import { apiJourney, monitor, step, expect } from '@elastic/synthetics';
apiJourney('orders API health', ({ request, params }) => {
monitor.use({ schedule: 1, locations: ['us_east'] });
let token: string;
step('obtain OAuth2 token', async () => {
const r = await request.post(`${params.authUrl}/oauth2/token`, {
form: {
grant_type: 'client_credentials',
client_id: params.clientId,
client_secret: params.clientSecret,
},
});
expect(r.status()).toBe(200);
token = (await r.json()).access_token;
});
step('check /orders', async () => {
const r = await request.get(`${params.apiUrl}/orders`, {
headers: { Authorization: `Bearer ${token}` },
});
expect(r.status()).toBe(200);
});
});API journeys share most of the regular journey() surface — step(), expect,
params, monitor.use(), apiJourney.skip / apiJourney.only — and integrate
with the same push command. They are pushed to Kibana as api-type monitors.
Each API journey gets its own isolated APIRequestContext with an independent
cookie jar; cookies set in one journey are not visible in another. All
playwrightOptions (baseURL, extraHTTPHeaders, ignoreHTTPSErrors,
httpCredentials, client certificates, proxy, etc.) flow through to the
underlying Playwright API context.
Per-request network data — URL, method, status, headers, body sizes, timings,
and (for HTTPS) the peer certificate's issuer / subject / validFrom /
validTo / protocol — is captured and emitted in the same journey/network_info
events used by browser journeys.
- Introduction
- CLI Options
- Syntax
- Creating a Journey
- Creating a Step
- Running via CLI
- Running via Heartbeat
Complete documentation is avaiable on elastic.co
Have questions? Want to leave feedback? Visit the Synthetics discussion forum.
Contributions are welcome, but we recommend that you take a moment and read our contribution guide first.
Made with