By default every Pug SDK sends data to Pug Cloud (https://api.pugs.dev). When you self-host, you change one thing: point the SDK’s endpoint at your own pug server. Nothing else about the API changes - same methods, same events. Keys don’t carry over from Pug Cloud: your instance issues its own, so create a project in your dashboard and use its project ID and keys.
Use the public URL of your server (the TLS-terminated origin in front of port 3000, e.g. https://api.example.com), not the internal :3000 address.
The one setting, per SDK
It’s the same option, endpoint, on every SDK (the HTTP API just uses the request URL):
| SDK | Package | Option | Default |
|---|---|---|---|
| Browser | @pug-sh/browser |
endpoint |
https://api.pugs.dev |
| Flutter | pug_flutter |
endpoint |
https://api.pugs.dev |
| Node (server) | @pug-sh/node |
endpoint |
https://api.pugs.dev |
| Ruby (server) | pug-sh |
endpoint |
https://api.pugs.dev |
| HTTP API | - | request URL | https://api.pugs.dev |
Browser
import { init } from '@pug-sh/browser'
init('YOUR_PROJECT_ID', {
apiKey: 'pub_YOUR_PUBLIC_KEY',
endpoint: 'https://api.example.com', // your pug server
})For environment-aware config (e.g. Vite), fall back to localhost in dev:
init(import.meta.env.VITE_PUG_PROJECT_ID, {
apiKey: import.meta.env.VITE_PUG_PUBLIC_KEY,
endpoint: import.meta.env.DEV ? 'http://localhost:3000' : 'https://api.example.com',
})The one-tag CDN install takes it as data-endpoint="https://api.example.com" - see One-tag install.
Flutter
import 'package:pug_flutter/pug_flutter.dart';
await Pug.init(
'YOUR_PROJECT_ID',
const PugOptions(
apiKey: 'pub_your_public_key',
endpoint: 'https://api.example.com', // your pug server
),
);Node (server-side)
The Node SDK takes the same endpoint option and authenticates with your private key (prv_...):
import { Pug } from '@pug-sh/node'
const pug = new Pug({
apiKey: process.env.PUG_PRIVATE_KEY!, // prv_... - server-side only
endpoint: 'https://api.example.com', // your pug server
})Ruby (server-side)
The Ruby SDK takes the same endpoint option and authenticates with your private key (prv_...):
require "pug"
pug = Pug::Client.new(
api_key: ENV.fetch("PUG_PRIVATE_KEY"), # prv_... - server-side only
endpoint: "https://api.example.com", # your pug server
)It doesn’t follow redirects, so use the URL your server answers on: an http:// origin that redirects to https:// fails every request as unimplemented, and dropped events go to on_error.
HTTP API
If you call the API directly, just swap the host in the request URL - the paths are unchanged. The key identifies the project, so the body carries only the events (HTTP API):
curl -X POST https://api.example.com/sdk.events.v1.EventsService/BatchCreate \
-H 'Content-Type: application/json' \
-H 'Connect-Protocol-Version: 1' \
-H 'x-api-key: pub_YOUR_PUBLIC_KEY' \
-d '{ "events": [ /* ... */ ] }'Notes
- No SDK-specific CORS setup is needed on the server. The events and profiles ingest services accept any origin (auth lives entirely in the
x-api-keyheader). Only the dashboard origin needs listing inPUG_CORS_ORIGINS- see Dashboard. - Key types are unchanged. Public keys (
pub_...) for the browser/Flutter SDKs, private keys (prv_...) for the Node and Ruby SDKs and server-to-server reads. See Authentication. - Verify it works: open your self-hosted dashboard’s Live view - an event fired from a repointed SDK should appear within ~15 seconds. If requests fail with a network error, confirm the endpoint is your public HTTPS origin and that TLS terminates in front of the server.
For the full initialization options (batching, sessions, auto-capture, consent), see the SDKs reference.
Further reading
- Dashboard: the dashboard’s own
VITE_API_BASE_URLendpoint setting - Deployment: stand up the server your SDKs point at
- Development: point SDKs at a local
pug dev