PNTR TestKit
Deterministic email and webhook tests
Wait for the exact email or webhook your Playwright, Cypress, or CI run triggered.
Your test waits once. PNTR watches.
Replace repeated inbox downloads and fixed sleeps with one filtered request that resolves when a matching event arrives.
Match the exact email
Filter by recipient, sender, subject, and the time your test started.
Match the exact request
Filter by method, path, header, body content, and a freshness boundary.
Fail on a real timeout
PNTR holds the wait on the server and returns HTTP 408 when nothing matches.
Use it inside the test you already run
Use the CLI in a shell step, or call PNTR's authenticated wait endpoints from any runner that can send HTTP requests.
POST /api/subdomains/:id/emails/waitrecipient="$(pntr recipient testbox.pntr.dev \ --prefix signup)"pntr email wait "$PNTR_EMAIL_SUBDOMAIN_ID" \ --to "$recipient" \ --subject "Verification code" \ --since 5m \ --timeout 20s \ --jsonpntr webhook wait "$PNTR_WEBHOOK_SUBDOMAIN_ID" \ --method POST \ --path /stripe \ --body-contains "payment_intent.succeeded" \ --since 5m \ --timeout 20s \ --jsonimport { randomUUID } from "node:crypto";const startedAt = new Date().toISOString();const recipient = `signup-${randomUUID()}@testbox.pntr.dev`;// Capture startedAt before the browser action triggers the email.await page.getByLabel("Email").fill(recipient);await page.getByRole("button", { name: "Sign up" }).click();const response = await request.post( `https://api.pntr.dev/api/subdomains/${ process.env.PNTR_EMAIL_SUBDOMAIN_ID }/emails/wait`, { headers: { Authorization: `Bearer ${process.env.PNTR_TOKEN}`, }, data: { recipient, subject_contains: "Verification code", since: startedAt, timeout_seconds: 20, }, timeout: 30_000, });expect(response.ok()).toBeTruthy();const message = await response.json();const content = message.body_text ?? message.body_html ?? "";const otp = content.match(/\b\d{6}\b/)?.[0];const startedAt = new Date().toISOString();// Trigger the provider's test-mode delivery after recording startedAt.await triggerTestPayment();const response = await fetch( `https://api.pntr.dev/api/subdomains/${ process.env.PNTR_WEBHOOK_SUBDOMAIN_ID }/requests/wait`, { method: "POST", headers: { Authorization: `Bearer ${process.env.PNTR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ method: "POST", path: "/stripe", body_contains: "payment_intent.succeeded", since: startedAt, timeout_seconds: 20, }), });if (!response.ok) throw new Error("Webhook did not arrive");const delivery = await response.json();expect(delivery.body_truncated).toBe(false);One test run, two isolated endpoints
pntr env create provisions -mail and -hook sibling subdomains. Each environment uses two subdomain quota slots.
pntr env create "ci-$GITHUB_RUN_ID" \ --output .pntr-test-env.json \ --jsonpntr env delete \ --manifest .pntr-test-env.json \ --confirm \ --jsonIsolate concurrent signup, OTP, and reset-password tests
Every local part reaches the catch-all inbox. Recipient and since filters make each run select only its own message.
POST ci-8421-hook.pntr.dev/stripe
Assert the delivery your action caused
Match a webhook by path, header, method, body content, and start time.
Put TestKit into a real workflow
Add one deterministic wait to your next test
Free includes 2 parallel waits. Premium includes 10.