smee-client: forwarding webhooks from the internet to a laptop
🔴 Receives payloads then sends them to your local server
At a glance
- What is it?
- smee-client is the client library and command line tool for smee.io, a service that receives webhook payloads on a public URL and forwards them to a local development server over a Server-Sent Events stream. It exists to solve one specific problem, receiving a GitHub or Stripe webhook on a machine that is behind a NAT, and the whole library is small enough to read in one sitting.
- Who is it for?
- Adopt smee-client if you are building a GitHub App or another webhook consumer on a laptop behind a router and need the events delivered in seconds rather than through a staging deploy, since the client is two dependencies, starts instantly and gives you the received events if you need to react to them.
- Can I use it commercially?
- Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem it solves
smee-client is the client and CLI for smee.io, a service that delivers webhooks to your local development environment. The repository description is even shorter: receives payloads, then sends them to your local server.
The problem is a specific and very common one. You are building a GitHub App, or integrating a Stripe webhook, or testing any service that pushes events to a URL you control. The service is on the internet and your code is on your laptop, behind a home router, on a network that does not have a stable public address. The service cannot reach you, and running it in production to test it is absurd.
The usual workarounds are all bad. You can use a tunnelling service, which means sending your development traffic through somebody else's infrastructure. You can deploy to a staging environment, which means the feedback loop is a deploy. Or you can write your own relay, which is a weekend gone and a service to keep running.
smee.io is the third option with the relay already written. A service on the internet holds a URL, the service that sends webhooks posts to that URL, and smee.io forwards what it receives down to your machine through a long-lived connection.
That last part is the mechanism, and it is what makes the design work. The connection is Server-Sent Events, so the browser-standard one-way server-push mechanism. The client opens a stream to smee.io, and events arrive over it as they happen. There is no polling and no webhook receiver to expose, which means there is no inbound port to open on your machine and nothing to configure in your router.
The project belongs to the Probot organisation, which is the GitHub App framework, and the README points to probot/smee.io for those who are looking for the service itself, separating the client from the server in a way that makes sense now that the client is the part people install.
The primary language is TypeScript, the licence is ISC, the default branch is master, and the homepage is smee.io. The last push was on 2026-10-19 with the project not archived. The recent releases include v5.0.0 from 2025-11-13 and v4.4.3 from 2025-11-25, so the version jumped a major number and has been stable since.
Installing it, and the two ways to use it
Installation is a single global npm install:
npm install -g smee-clientThen there are two usage modes, and the choice between them is about whether you are driving the client or embedding it.
The command line mode is the smee command, which forwards webhooks from smee.io to your local development environment:
smeeRun smee --help for usage. The CLI is the path for a person who wants a webhook arriving on their laptop while they work on it, and it is the one that needs no code at all.
The Node client is the other mode, and it is where the shape of the library is visible:
import SmeeClient from 'smee-client'
const smee = new SmeeClient({
source: 'https://smee.io/abc123',
target: 'http://localhost:3000/events',
logger: console
})
const events = await smee.start()
// Stop forwarding events
await smee.close()Four things are worth reading in that snippet. The options are a source, which is the smee.io URL for your channel, a target, which is your local endpoint, and a logger. The start method is awaited and returns the events, so the caller can process them if it wants to rather than only relying on the forwarding. And close stops the forwarding, which matters for a script that runs for a while and then needs to shut down cleanly.
The distinction matters more than it might seem. A pure forwarder and a client that hands you the events are two different contracts, and the second one is what you want if your application needs to react to the event rather than just receive it, for example to trigger a test run. The README shows the forwarding behaviour, and the events array is the hook for anything more.
There is also a section on proxy servers, and it is one line long but load-bearing. By default, the SmeeClient API client makes use of the standard proxy server environment variables.
That matters in corporate environments, where an HTTP proxy is how you reach the internet at all. Because the client honours the conventional environment variables, it works behind a proxy without any extra configuration, which is a property worth more than it sounds when your development machine sits inside a network that requires one.
The package, and what the dependency list says
The package manifest is small and tells you the whole story of the implementation.
There are exactly two runtime dependencies. eventsource at about 4.0.0 is the Server-Sent Events client, and undici at about 7.0.0 is the HTTP client from Node's own team, which is what forwards the received payloads to your local target. So the library is a small amount of glue between an SSE stream and an HTTP POST, which is exactly what the problem needs.
The bin entry maps the smee command to bin/smee.js, and the files list publishes index.js, index.d.ts and the bin directory. The type declaration file is included, so the package ships with its own types, which for a library consumed from TypeScript is the difference between autocomplete and a lookup in the documentation.
The scripts are worth reading because they show the project's own standards. The build is tsc -p tsconfig.json, so it is a plain TypeScript compile with no bundler. The lint and lint:fix scripts run prettier over index.ts, the test files, bin/smee.js, package.json and tsconfig.json, with an --end-of-line auto flag, which tells you the project accepts either line ending style and normalises to whatever is in the files. And the test scripts are vitest, in three forms: a plain run, a run with coverage, and a development mode with the vitest UI and coverage together.
The dev dependencies follow the same pattern. @vitest/coverage-v8 for coverage, get-port at 7.1.0 which is the standard way to grab a free port for a test server, prettier for formatting, typescript at 5, vitest at 4, and @octokit/tsconfig, which is the shared TypeScript configuration from the Octokit project, so the project uses a base config rather than writing its own compiler options from scratch. @types/node at 24 tracks the current Node type definitions.
The engines field says node ^20.18 or >= 22, so the client supports the current Node release lines and not the older ones that have left support. And the publishConfig sets access to public and provenance to true, which turns on npm's provenance attestations so that a published version can be traced back to the commit and workflow that built it.
The repository, and the design choices behind a small library
The top-level listing is short: .github/ for the workflows, index.ts as the single source file, bin/ for the command line entry point, test/ for the tests, plus the usual configuration.
One source file for the whole client is a deliberate choice and it is worth understanding why. A webhook forwarder is conceptually two operations, subscribe to a stream and post what arrives. Everything else, the options, the logging, the event handling, is a small amount of code around those two. Splitting it across modules would add import structure and a build graph without adding clarity.
The tooling supports that reading. The build is a single tsc invocation rather than a bundler, the lint target lists the files explicitly rather than a directory glob, and the test suite lives in one directory. This is a project that has been kept at the size where a directory glob would hide which files matter.
The codecov.yml at the root and the Codecov badge mean coverage is measured and published, and the CI workflow badge points at a ci.yml workflow, so the tests and the lint both run on every change.
Nowhere in the manifest is there a heavyweight HTTP framework, an Express-style middleware stack, or a CLI argument parser with a dozen dependencies. The client depends on two packages and the CLI on those same two. The whole point of a tool like this is that it starts instantly and stays out of the way, and the dependency list is where that shows up.
The licence is ISC, which is a permissive licence close to MIT in effect, with a short permission grant and a warranty disclaimer. For a package published from a public repository with provenance enabled, that combination is a reasonable, low-friction choice.
One detail that says something about the project's maturity is the version field in the manifest, which reads 0.0.0-development. The published versions are set by the release tooling rather than by hand, and the manifest in the repository is not the version you would install. That is the standard pattern for a package with an automated release process, and it is why the recent releases list a v5.0.0 while the manifest says 0.0.0-development.
How to evaluate it, and when to reach for something else
There are four things to consider, and the first is whether you need a hosted relay at all.
If you are on a network with a stable public address, or you already run a tunnel, or you deploy to staging, you do not need this. The value of smee.io is specifically for the developer on a laptop behind a router who needs to receive a webhook in seconds rather than minutes.
Second, what you are sending through it. A webhook payload may contain customer data, a signature, a personal identifier. It travels to smee.io over the network, and from there to your machine. That is an extra party in the path compared with a direct tunnel, and if the payload is sensitive you should think about whether a self-hosted relay or a tunnelling service under your own control suits you better. The client is a client for a hosted service, and the security question is about the service, not the client.
Third, the API surface. Four options, start, close, source and target, with the logger for output. That is a small contract, and small contracts are good, but it does mean there is no built-in filtering, no retry policy, no transformation and no persistence of received events. If your use case needs any of those, you build them on top of the events array the start method returns.
Fourth, the two usage modes. If you are testing by hand, the CLI is enough. If you are writing an integration test that needs the webhook delivered at a specific moment, the Node client with its returned events array is the right entry point, and the get-port dev dependency tells you the project's own tests spin up a server on a free port rather than a fixed one, which is the practice you would want to copy.
What you get is a dependency-light client that solves one problem completely and exposes the events if you need more. For a tool in the development path, where it runs on your machine and forwards test traffic, that is the right size, and the ISC licence plus npm provenance mean adopting it carries little legal weight either way.
Editorial conclusion
Adopt smee-client if you are building a GitHub App or another webhook consumer on a laptop behind a router and need the events delivered in seconds rather than through a staging deploy, since the client is two dependencies, starts instantly and gives you the received events if you need to react to them. Do not reach for it if your payload carries sensitive customer data and you cannot accept a hosted relay in the path, because smee.io sits between the sender and your machine and the security question is about that service rather than the client. Before you commit, use the smee CLI to confirm the round trip works before writing any code against the events array, and note that the client offers no filtering, retry or persistence of its own, so anything beyond plain forwarding has to be built on top of the events that start returns.
Frequently asked questions
What is smee-client?
It is the client library and command line tool for smee.io, a service that delivers webhooks to your local development environment. The client receives payloads on a public smee.io URL and forwards them to a local server over a Server-Sent Events stream, and it is MIT-style ISC licensed and written in TypeScript.
How do I install smee-client and start forwarding?
Install it globally with npm install -g smee-client, then run the smee command to forward webhooks from smee.io to your local development environment. Run smee --help for usage. There is also a Node client, imported as SmeeClient from smee-client, constructed with a source, a target and a logger, started with await smee.start() and stopped with await smee.close().
Which Node versions does smee-client support?
The engines field requires node ^20.18 or >= 22, so the current Node release lines are supported and older ones are not. The package publishes with public access and provenance enabled, so a released version can be traced back to the commit and workflow that built it.
What does smee-client depend on?
Two runtime dependencies only. eventsource at about 4.0.0 handles the Server-Sent Events connection, and undici at about 7.0.0 is the HTTP client that forwards the received payloads to your local target. Development dependencies include vitest for testing, prettier for formatting, typescript for the tsc build and get-port for grabbing a free port in tests.
Does smee-client work behind a corporate proxy?
Yes. By default, the SmeeClient API client makes use of the standard proxy server environment variables, so it works in a network that requires an HTTP proxy without extra configuration.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/probot-smee-client)Was this useful?