Documentation

Everything you need to capture production errors, open GitHub issues, and hand your agent source-aware repair context.

Add Patchly with your coding agent

Using Claude Code, Codex, Antigravity, or another agentic editor?

Point the agent at the Patchly skills guide so it can install the SDK, wire browser and server capture, and upload source maps without guessing. Project-specific skills URLs are available from each project's settings page.

Install

npm install patchly@latest

Minimum version: patchly@0.2.2. Earlier releases are missing serverless flush support, breadcrumbs, network/console auto-capture, the React error boundary, or the no-op-on-missing-DSN guard. If you have an older version pinned, run npm install patchly@latest to upgrade. Also works with pnpm, yarn, or bun.

Quick start

The shortest Patchly setup loop is: capture runtime failures with the SDK, let Patchly open the GitHub issue, then use the repair note as your agent's starting context.

import { init } from "patchly";

const patchly = init({
  dsn: process.env.PATCHLY_DSN,        // required — from patchly.cc → Settings
  release: process.env.VERCEL_GIT_COMMIT_SHA, // optional — enables source map resolution
  userId: "user_123",                   // optional — track affected users
  autoCapture: true,                    // default: true — captures uncaught errors
});

// manual capture
patchly.capture(new Error("something broke"));
await patchly.flush();

// manual breadcrumb
patchly.addBreadcrumb({ category: "custom", message: "User clicked checkout" });

// flush on shutdown (Node.js)
await patchly.close();

DSN & API keys

When you create a project in the Patchly dashboard, you get two things from the Settings page:

  • DSN — used by the SDK to send error events
  • API key — used for source map uploads and API access

Where to find them

Go to patchly.cc → Dashboard → open your project → Settings tab → API keys section. Click Generate new key to create one, then copy it immediately (it won't be shown again).

DSN format

Your DSN embeds the API key and project ID in a single URL:

https://<API_KEY>@api.patchly.cc/<PROJECT_ID>

The <API_KEY> portion (the psk_... string) is the same API key shown in Settings. You can use it in two ways:

  • As a DSN — pass the full URL to init({ dsn}) for the SDK
  • As an auth token — pass just the psk_... key to the build plugin's authToken option for source map uploads

Environment variables

# .env.local (or .env)
# SDK — full DSN URL
PATCHLY_DSN=https://psk_abc123@api.patchly.cc/your_project_id
NEXT_PUBLIC_PATCHLY_DSN=https://psk_abc123@api.patchly.cc/your_project_id

# Build plugin — just the API key (the psk_... part from your DSN)
PATCHLY_AUTH_TOKEN=psk_abc123

All three env vars use the same underlying API key — the DSN just wraps it in a URL. Never commit these to version control.

Next.js (App Router)

Singleton pattern

Create a shared Patchly instance so the SDK is initialized exactly once, even when modules are re-imported across server and client boundaries.

// lib/patchly.ts
import { init } from "patchly";

export const patchly = init({
  dsn: process.env.NEXT_PUBLIC_PATCHLY_DSN,
  release: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
});

Server-side — instrumentation.ts

Create at your project root:

import type { Instrumentation } from "next";

let patchly: ReturnType<typeof import("patchly").init> | null = null;

async function getPatchly() {
  if (process.env.NEXT_RUNTIME !== "nodejs" || !process.env.PATCHLY_DSN) return null;
  if (patchly) return patchly;

  const { init } = await import("patchly");
  patchly = init({
    dsn: process.env.PATCHLY_DSN,
    release: process.env.VERCEL_GIT_COMMIT_SHA,
  });
  return patchly;
}

export async function register() {
  await getPatchly();
}

export const onRequestError: Instrumentation.onRequestError = async (error) => {
  const client = await getPatchly();
  if (!client) return;

  client.capture(error);
  await client.flush();
};

For Next.js 15+, onRequestError is the important hook: Next catches route-handler, Server Action, and render errors before they become process-level uncaught exceptions.

Client-side — app/error.tsx

"use client";
import { useEffect } from "react";
import { patchly } from "@/lib/patchly";

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  useEffect(() => { patchly.capture(error); }, [error]);
  return (
    <div>
      <h2>Something went wrong</h2>
      <button onClick={reset}>Try again</button>
    </div>
  );
}

Environment variables

# .env.local
PATCHLY_DSN=https://<key>@api.patchly.cc/<projectId>
NEXT_PUBLIC_PATCHLY_DSN=https://<key>@api.patchly.cc/<projectId>

Next.js gotchas

  • Use the instrumentation singleton above — importing init() in multiple files is fine because the SDK returns the existing instance if already initialized.
  • Server Components cannot use NEXT_PUBLIC_ env vars. Use PATCHLY_DSN (without prefix) in instrumentation.ts and server-only code.
  • The SDK gracefully no-ops when dsn is undefined — safe to import during static prerendering and CI builds.
  • For App Router, prefer error.tsx files over <PatchlyErrorBoundary> — the framework already provides the error boundary.

Node.js / Express

import { init } from "patchly";

const patchly = init({ dsn: process.env.PATCHLY_DSN });

// Uncaught exceptions are captured automatically.
// For manual capture:
try {
  riskyOperation();
} catch (err) {
  patchly.capture(err as Error);
  await patchly.flush();
}

// Graceful shutdown
process.on("SIGTERM", async () => {
  await patchly.close();
  process.exit(0);
});

React Error Boundary

For standalone React apps (not using Next.js App Router), Patchly ships a drop-in error boundary that captures render-time errors automatically.

import { PatchlyErrorBoundary } from "patchly/react";

function App() {
  return (
    <PatchlyErrorBoundary fallback={<p>Something went wrong</p>}>
      <MyApp />
    </PatchlyErrorBoundary>
  );
}

The fallback prop accepts a React node or a render function with access to the error and a reset callback:

<PatchlyErrorBoundary
  fallback={({ error, reset }) => (
    <div>
      <p>Error: {error.message}</p>
      <button onClick={reset}>Try again</button>
    </div>
  )}
  onError={(error, errorInfo) => {
    console.log("Caught by Patchly:", error.message);
  }}
>
  <MyApp />
</PatchlyErrorBoundary>

Works with React 18 and React 19. The react and react-dom peer dependencies are optional — Node-only apps that don't import patchly/react won't need React installed.

Capture hooks

Beyond uncaught exceptions and unhandled promise rejections, the SDK automatically captures two additional error sources. Both are enabled by default.

console.error capture

Many frameworks (React, Next.js, Express) log errors to console.error without throwing. The SDK monkey-patches console.error to capture these automatically. The original console.error is always called first, so your existing logging is unaffected.

Deduplication is built in — if an error is already captured via an uncaught exception handler, the same error logged to console.error won't produce a duplicate event.

init({
  dsn: process.env.PATCHLY_DSN,
  captureConsoleErrors: false, // disable if needed
});

Network error capture

The SDK intercepts fetch() (browser + Node) and XMLHttpRequest (browser only) to capture failed requests — HTTP 4xx/5xx responses, network errors, and timeouts. Each captured event includes the HTTP method, URL, status code, and response time.

Requests to the Patchly ingest endpoint are automatically excluded to prevent infinite loops.

init({
  dsn: process.env.PATCHLY_DSN,
  captureNetworkErrors: false, // disable if needed
});

Source maps

Upload source maps so Patchly can show original file names and line numbers in stack traces. Use the @patchly/build-plugin package to automate uploads as part of your build.

npm install -D @patchly/build-plugin

The authToken in every example below is your API key (the psk_... string from Settings). Store it as PATCHLY_AUTH_TOKEN in your environment.

Webpack

// webpack.config.js
const { PatchlyWebpackPlugin } = require("@patchly/build-plugin/webpack");

module.exports = {
  plugins: [
    new PatchlyWebpackPlugin({
      authToken: process.env.PATCHLY_AUTH_TOKEN,
    }),
  ],
};

Vite

// vite.config.ts
import { patchlyVite } from "@patchly/build-plugin/vite";

export default defineConfig({
  plugins: [
    patchlyVite({
      authToken: process.env.PATCHLY_AUTH_TOKEN,
    }),
  ],
});

Next.js (Turbopack)

// next.config.mjs
import { withPatchlySourceMaps } from "@patchly/build-plugin/turbopack";

const nextConfig = { /* your config */ };

export default withPatchlySourceMaps(nextConfig, {
  authToken: process.env.PATCHLY_AUTH_TOKEN,
});

The plugin auto-detects the release tag from CI environment variables (VERCEL_GIT_COMMIT_SHA, GITHUB_SHA, etc.) or falls back to git rev-parse HEAD. You can override it with the release option. By default, .map files are deleted from the build output after upload.

Build plugin options

OptionTypeDescription
authTokenstringRequired. Your API key (psk_...) from Settings. Use env var PATCHLY_AUTH_TOKEN.
releasestring?Release identifier. Auto-detected from CI env vars.
deleteSourceMapsboolean?Default true. Delete .map files after upload.
endpointstring?Ingest endpoint. Default: https://api.patchly.cc
silentboolean?Default false. Suppress all console output.

GitHub Issues

Automatic issue creation

When an error group reaches 3 events and Patchly generates an AI fix, a GitHub issue is automatically created on your project's configured repository. The issue includes the error title, event count, timestamps, and the AI fix suggestion. No manual action needed.

Manual issue creation

You can also file a GitHub issue manually from the error detail page using the Create GitHub Issue button. The issue includes the full error context: event count, affected users, stack trace, AI-generated fix, and affected files.

Setup

To use GitHub issues (automatic or manual), your project needs:

  • A GitHub repository configured in your project settings (owner + repo name)
  • Your account authenticated with GitHub OAuth (the repo scope is required for private repos)

Issues are created with the bug and patchly labels, and include a link back to your Patchly dashboard. Each error group can only have one linked issue — the button changes to "View Issue" after creation.

AI fix suggestions

When an error group reaches 3 events, Patchly automatically fetches your source code from GitHub and generates a fix suggestion using an LLM. Fix suggestions include:

  • Root cause analysis
  • Suggested code fix
  • Affected files
  • Confidence score (0-1)

View and vote on fixes in the dashboard.

Alerts

Configure Slack webhook or email alerts in Settings. Alerts fire when an error group exceeds your threshold count within a time window.

SDK API reference

MethodDescription
init(config)Initialize the SDK. Returns { capture, addBreadcrumb, close }.
capture(error)Manually capture an Error instance.
addBreadcrumb(crumb)Record a breadcrumb attached to the next captured error.
close()Flush pending events and remove global handlers.

Config options

OptionTypeDescription
dsnstringRequired. Your project DSN.
releasestring?App version or commit SHA. Enables source map resolution.
userIdstring?User identifier for affected-user tracking.
autoCaptureboolean?Default true. Auto-capture uncaught errors.
captureConsoleErrorsboolean?Default true. Capture errors logged to console.error.
captureNetworkErrorsboolean?Default true. Capture failed fetch/XHR requests.
enableBreadcrumbsboolean?Default true. Record breadcrumbs leading up to errors.
maxBreadcrumbsnumber?Default 20. Max breadcrumbs retained in the circular buffer.

Claude Code integration

Patchly ships an agent skills guide for Claude Code, Codex, Antigravity, and other agentic editors. Add it to your project so your agent follows the same capture, issue, repair workflow without guessing:

curl -o PATCHLY.md https://patchly.cc/SKILLS.md

Read the public guide at /docs/skills. Project-specific skills URLs with your DSN pre-filled are available from each project's settings page.

Ready to get started?

Create a free account and start tracking errors in under a minute.

Report a bug