
Company News
AWS Security Hub Adds Socket for Supply Chain Security
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.
Instant tunnels and webhooks for AI agents. Pay per use with prepaid credits.
# One-time: log in (opens your browser)
npx otterkit login
# Create a tunnel
npx otterkit tunnel 3000
Buy credits at console.otterkit.com. Once you've logged in, agents on the same machine provision automatically using your credits.
Expose a local port to the internet.
npx otterkit tunnel <port> # Foreground (1 credit/hr)
npx otterkit tunnel <port> --log # Also capture every request to a JSONL log
npx otterkit tunnel <port> --daemon # Background, auto-stops after 24h
npx otterkit tunnel <port> --daemon --ttl 3d # Background, auto-stops after 3 days
npx otterkit tunnel <port> --subdomain myapp # Stable URL: https://myapp.otterkit.app
npx otterkit tunnel <port> --auth user:pass # Require HTTP Basic auth on the public URL
--auth is enforced by the CLI on your machine, before anything reaches your local
server - unauthenticated requests get a 401 and are never forwarded. Credentials
are not sent to or stored by OtterKit's servers.
By default each tunnel gets a fresh random URL (https://tunnel-a1b2c3d4.otterkit.app).
Pass --subdomain <name> to get a stable, reserved URL instead. The name is claimed to
your account the first time you use it and reused on every run after that - so the same
public URL (https://myapp.otterkit.app) always points at your tunnel, across machines and
restarts. It works on tunnel and webhook, foreground and --daemon.
Holding a name is free - you still only pay the normal 1 credit/hr while a tunnel is connected. If another account already owns the name you'll get a "taken" error and no credit is charged; pick another name or omit the flag for an auto name.
npx otterkit subdomains # List your reserved subdomains
npx otterkit subdomains reserve myapp # Reserve a name without starting a tunnel
npx otterkit subdomains release myapp # Release a name so it can be reused
otterkit up)Define named tunnel profiles in an otterkit.toml and bring them all up with one command.
Every profile runs as a background daemon, so status, inspect, and stop work as usual.
# otterkit.toml
[tunnels.web]
port = 3000
subdomain = "myapp" # stable URL (optional)
auth = "admin:s3cret" # HTTP Basic auth (optional)
log = true # capture requests (optional)
ttl = "8h" # auto-stop (optional, default 24h)
[tunnels.hooks]
webhook = true # capture-only endpoint, no local server
subdomain = "myapp-hooks"
respond = 200 # custom auto-response (optional)
respond_body = '{"ok":true}'
npx otterkit up # Start everything (skips profiles already running)
npx otterkit up --json # Machine-readable results
npx otterkit down # Stop the daemons started from the config
npx otterkit up --config ./deploy/otterkit.toml
Capture incoming HTTP requests without a local server. Every request is saved to
~/.otterkit/requests/<subdomain>.jsonl so nothing is lost when the terminal closes.
npx otterkit webhook # Foreground (1 credit/hr)
npx otterkit webhook --daemon --ttl 4h # Background, auto-stops after 4h
Some providers require a specific response before they deliver events (challenge echoes,
strict 2xx checks). Customize the auto-response with --respond / --respond-body
(default: 200 {"received":true}):
npx otterkit webhook --respond 204
npx otterkit webhook --respond 200 --respond-body '{"challenge":"accepted"}'
With --standby, the endpoint stays live while you're disconnected (laptop closed,
daemon killed): the server answers each request with your auto-response and buffers up
to 200 captures (64 KB bodies), replaying them into your local log on reconnect -
providers never see downtime. Billing continues while disconnected (still capped at
10 credits/day; the session still auto-stops at its --ttl).
npx otterkit webhook --standby
npx otterkit webhook --daemon --standby --respond 204
View requests captured by webhook or tunnel --log.
npx otterkit inspect <subdomain> # Pretty-print last 20 requests
npx otterkit inspect <subdomain> --json # Raw JSONL (pipe-friendly)
npx otterkit inspect <subdomain> --last 50 # Show last 50 requests
npx otterkit inspect <subdomain> --follow # Live-tail new requests (Ctrl+C to stop)
npx otterkit inspect <subdomain> --method POST --status 5xx --path /webhook
npx otterkit inspect <subdomain> --har > session.har # HAR 1.2 for devtools/HAR viewers
Filters (--method, --status, --path) combine and work with --json, --follow,
and --har. --status accepts an exact code (500) or a class (5xx).
Re-send a captured request to your local server - same as the desktop app's replay,
straight to the target with no tunnel round-trip. Great for re-testing webhook handlers
after a fix without waiting for the provider to fire again. The replayed exchange is
appended to the capture log, so it shows up in otterkit inspect.
The target defaults to the running daemon's host:port for that subdomain; pass
--target for webhook captures or stopped tunnels.
npx otterkit replay <subdomain> # Re-send the latest request
npx otterkit replay <subdomain> --index 3 # Re-send request #3 (1 = oldest, -1 = latest)
npx otterkit replay <subdomain> --target 127.0.0.1:3000 # Explicit local target
npx otterkit replay <subdomain> --json # Machine-readable response (base64 body)
# Tweak the request before re-sending
npx otterkit replay <subdomain> --method PUT --path /v2/hook \
-H "X-Debug: 1" --body '{"event":"payment.failed"}'
# Edit individual JSON body fields (repeatable; numbers/booleans parse as JSON)
npx otterkit replay <subdomain> --set data.amount=999 --set data.livemode=false
Capture logs record the full exchange - response status, headers, and body - so
inspect --json and --har exports carry both sides of every request.
Block until a matching request arrives - the scriptable primitive for CI and agents:
"trigger the event, await its arrival, assert on the payload." By default it keeps
streaming matches (your session keeps running either way - await never stops the
tunnel it watches). Pass --count for a deterministic exit.
npx otterkit await <subdomain> # Stream every request (Ctrl+C to stop)
npx otterkit await <subdomain> --count 1 --timeout 120s # CI: exit 0 on arrival, 2 on timeout
npx otterkit await <subdomain> --method POST --path /stripe --json
Exit codes: 0 when --count is satisfied (or on Ctrl+C), 2 when --timeout
expires unmet. Filters (--method, --status, --path) match inspect's.
Started on otterkit.com/try? The page shows a claim command that moves the session into your terminal - same public URL, and everything already captured on the page is imported into your local log:
npx otterkit webhook --claim <code> # code comes from the /try page
Develop a webhook handler with no provider account, dashboard, or real event. send fires a
correctly-signed synthetic event at your local handler; verify checks whether captured requests
carry a valid signature. Both run locally - the secret never leaves your machine. Schemes: Stripe,
GitHub, Shopify, Slack.
# Fire a signed event straight at your handler (passes its signature check)
npx otterkit send stripe:payment_intent.succeeded 127.0.0.1:3000/webhooks/stripe --secret whsec_...
npx otterkit send github:push 127.0.0.1:3000/hooks --secret ghs_...
npx otterkit send --list # all providers + events
# "Is my verification wrong, or the payload?" - answered against captured requests
npx otterkit verify myhooks stripe --secret whsec_...
# Replay a captured request with an edited body, re-signed so it still validates
npx otterkit replay myhooks --set data.object.amount=999 --resign stripe --secret whsec_...
otterkit mcp runs a Model Context Protocol server over stdio - Claude Code,
Cursor, and any MCP client get tunnels, webhooks, capture logs, await, and
replay as native tools, authenticated by the same otterkit login.
# Claude Code
claude mcp add otterkit -- npx otterkit mcp
// Cursor & friends (.cursor/mcp.json)
{ "mcpServers": { "otterkit": { "command": "npx", "args": ["otterkit", "mcp"] } } }
Tools: webhook_create, tunnel_create, sessions_list, session_stop,
requests_list, request_await, request_replay, event_send, request_verify,
try_claim, account_status.
npx otterkit login # Log in via browser
npx otterkit whoami # Show account + balance
npx otterkit balance # Show credit balance
npx otterkit logout # Remove the saved token
npx otterkit status # List running daemons
npx otterkit stop <subdomain> # Stop a daemon
Every read/provision command takes --json and prints one machine-readable result:
npx otterkit tunnel 3000 --daemon --json # {"subdomain":..., "publicUrl":..., "pid":...}
npx otterkit webhook --daemon --json
npx otterkit up --json
npx otterkit status --json
npx otterkit subdomains list --json
npx otterkit inspect <subdomain> --json
npx otterkit replay <subdomain> --json
npx otterkit whoami --json
npx otterkit balance --json
Errors in --json mode are also JSON ({"error":"insufficient_credits", ...}) with exit
code 1.
| Flag | Description | Default |
|---|---|---|
--host <host> | Local host to forward to | 127.0.0.1 |
--log | Capture requests to a JSONL log (tunnel only) | off |
--daemon | Run in background | off |
--ttl <duration> | Auto-stop after duration, e.g. 4h, 3d (max 7d) | 24h |
--subdomain <name> | Use a stable reserved URL (claimed on first use) | auto |
--auth <user:pass> | Require HTTP Basic auth (tunnel only, enforced locally) | off |
--respond <status> | Webhook auto-response status (webhook only) | 200 |
--respond-body <d> | Webhook auto-response body (webhook only) | {"received":true} |
--claim <code> | Continue a /try page session (webhook only) | - |
--set <path=value> | Edit a JSON body field before replaying (repeatable) | - |
--count <n> | await: exit 0 after N matches | stream forever |
--timeout <dur> | await: give up after e.g. 30s, 5m (exit 2) | none |
--json | Machine-readable output (daemon/read commands) | off |
Metered by connected time. 1 credit = $0.01. Buy prepaid credits in the console.
| What | Cost |
|---|---|
| Per connected hour | 1 credit ($0.01), first hour up front |
| Daily cap per tunnel | 10 credits ($0.10) - hours beyond are free |
| While disconnected | Free - billing pauses |
| Auto-stop | Default 24h, --ttl up to 7d |
A forgotten tunnel bills at most the daily cap, then auto-stops at its TTL.
otterkit login runs a browser device-flow and saves an API token to ~/.otterkit/credentials.json.https://tunnel-a1b2c3d4.otterkit.app (or your
reserved --subdomain name, if given).For headless/CI, set OTTERKIT_TOKEN (create a token at console.otterkit.com) instead of otterkit login.
MIT
FAQs
OtterKit CLI - provision and connect tunnels for AI agents
The npm package otterkit receives a total of 1,417 weekly downloads. As such, otterkit popularity was classified as popular.
We found that otterkit demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.

Research
/Security News
Popular npm packages keyv and cacheable compromised.

Security News
A misconfiguration gave three Anthropic models internet access, and one, believing it was in a simulation, shipped a credential-stealing package to PyPI.