SDK setup
Okapi speaks the Sentry ingest wire format. Most Sentry SDKs work by changing only the DSN — no okapi-specific SDK, no extra configuration.
The DSN model
Okapi authenticates ingest by the DSN public key only — there is no secret component. The DSN format is:
http://<PUBLIC_KEY>@<host>/<project>
<PUBLIC_KEY>— from the project's DSN Keys tab in the dashboard.<host>— where Okapi is reachable, e.g.localhost:8480locally or your production domain.<project>— a trailing path segment. Okapi identifies the project by the key alone and ignores this value, but some SDKs (e.g.sentry-python) validate it as an integer, so use a number like1. The DSN Keys tab shows a ready-to-copy DSN already in this form.
For production, use https:// and your domain, fronted by a TLS-terminating reverse proxy — see Reverse proxy and TLS.
Ingest endpoints
Okapi exposes both Sentry ingest endpoints:
POST /api/{project_id}/store/— the legacy single-event endpoint.POST /api/{project_id}/envelope/— the modern envelope endpoint, with CORS support for browser SDKs. It also carries transactions and spans for performance monitoring. The legacy endpoint accepts errors only and answers400to a transaction.
Both endpoints accept a Content-Encoding of identity, gzip, x-gzip, deflate, br (Brotli) or zstd, and at most two of them stacked, with br only as the last one listed. Any other Content-Encoding gets 415.
Your SDK picks the right one automatically based on its version — you never call these directly.
Per-platform setup
These excerpts show the install step and the first initialization file. Frameworks require additional files from the linked full guide. Complete those steps before you start the app.
Node.js
npm install @sentry/node
instrument.mjs
import * as Sentry from "@sentry/node";
Sentry.init({
dsn: "http://YOUR_PUBLIC_KEY@localhost:8480/1",
environment: "production",
});
Load instrument.mjs before other modules with node --import ./instrument.mjs app.mjs (Node 20.19+).
Full Node.js guide — complete the remaining configuration and send a test event.
Python
pip install sentry-sdk
main.py
import sentry_sdk
sentry_sdk.init(
dsn="http://YOUR_PUBLIC_KEY@localhost:8480/1",
environment="production",
)
Full Python guide — complete the remaining configuration and send a test event.
Browser JavaScript
npm install @sentry/browser
src/main.js
import * as Sentry from "@sentry/browser";
Sentry.init({
dsn: "http://YOUR_PUBLIC_KEY@localhost:8480/1",
environment: "production",
});
For React apps, use @sentry/react with the same initialization options.
Full Browser JavaScript guide — complete the remaining configuration and send a test event.
Laravel
composer require sentry/sentry-laravel
php artisan vendor:publish --provider="Sentry\Laravel\ServiceProvider"
.env
SENTRY_LARAVEL_DSN=http://YOUR_PUBLIC_KEY@localhost:8480/1
SENTRY_ENVIRONMENT=production
Full Laravel guide — complete the remaining configuration and send a test event.
PHP
composer require sentry/sentry
public/index.php
// After require __DIR__.'/../vendor/autoload.php';
\Sentry\init([
'dsn' => 'http://YOUR_PUBLIC_KEY@localhost:8480/1',
'environment' => 'production',
]);
Full PHP guide — complete the remaining configuration and send a test event.
Next.js
npm install @sentry/nextjs
sentry.server.config.ts
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "http://YOUR_PUBLIC_KEY@localhost:8480/1",
environment: "production",
});
For Next.js 15.3+ App Router, put instrumentation.ts next to app (or src/app), with the other runtime config files beside it; merge next.config.ts into your existing config. On Next.js 15.0–15.2, this guide covers server capture only; client-side capture needs Next.js 15.3+. Next.js 14 is not covered by this guide (it needs next.config.mjs and experimental.instrumentationHook).
Full Next.js guide — complete the remaining configuration and send a test event.
Go
go get github.com/getsentry/sentry-go
main.go
package main
import (
"log"
"time"
"github.com/getsentry/sentry-go"
)
func main() {
if err := sentry.Init(sentry.ClientOptions{
Dsn: "http://YOUR_PUBLIC_KEY@localhost:8480/1",
Environment: "production",
}); err != nil {
log.Fatalf("sentry.Init: %s", err)
}
// Flush buffered events before the program exits.
defer sentry.Flush(2 * time.Second)
// ...
}
Full Go guide — complete the remaining configuration and send a test event.
Ruby on Rails
Gemfile
gem "sentry-ruby"
gem "sentry-rails"
config/initializers/sentry.rb
Sentry.init do |config|
config.dsn = "http://YOUR_PUBLIC_KEY@localhost:8480/1"
config.environment = "production"
end
Full Ruby on Rails guide — complete the remaining configuration and send a test event.
Spring Boot
build.gradle
// Spring Boot 3 (Jakarta), inside dependencies
implementation 'io.sentry:sentry-spring-boot-starter-jakarta:8.62.0'
For Kotlin DSL, add this dependency inside build.gradle.kts instead:
implementation("io.sentry:sentry-spring-boot-starter-jakarta:8.62.0")
src/main/resources/application.properties
sentry.dsn=http://YOUR_PUBLIC_KEY@localhost:8480/1
sentry.environment=production
Full Spring Boot guide — complete the remaining configuration and send a test event.
ASP.NET Core
dotnet add package Sentry.AspNetCore
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseSentry(options =>
{
options.Dsn = "http://YOUR_PUBLIC_KEY@localhost:8480/1";
options.Environment = "production";
});
Full ASP.NET Core guide — complete the remaining configuration and send a test event.
Rust
Cargo.toml
[dependencies]
sentry = "0.49"
src/main.rs
fn main() {
let _guard = sentry::init(
sentry::ClientOptions::new()
.dsn("http://YOUR_PUBLIC_KEY@localhost:8480/1")
.environment("production"),
);
// ... start tokio here, not via #[tokio::main], so Sentry inits first
}
Full Rust guide — complete the remaining configuration and send a test event.
Phoenix
mix.exs
# Inside deps, then run mix deps.get
{:sentry, "~> 14.0"},
{:finch, "~> 0.21"}
# Elixir < 1.18 also needs: {:jason, "~> 1.4"}
config/prod.exs
config :sentry,
dsn: "http://YOUR_PUBLIC_KEY@localhost:8480/1",
environment_name: "production",
# We expect this logger to catch Bandit crashes: PlugCapture is Cowboy-only.
# Phoenix defaults to Bandit; this is an inference from the SDK logger setup.
logs: []
Full Phoenix guide — complete the remaining configuration and send a test event.
Vue
npm install @sentry/vue
src/main.js
import { createApp } from "vue";
import * as Sentry from "@sentry/vue";
import App from "./App.vue";
const app = createApp(App);
Sentry.init({
app,
dsn: "http://YOUR_PUBLIC_KEY@localhost:8480/1",
environment: "production",
});
app.mount("#app");
Full Vue guide — complete the remaining configuration and send a test event.
Nuxt
npm install @sentry/nuxt
sentry.server.config.ts
import * as Sentry from "@sentry/nuxt";
Sentry.init({
dsn: "http://YOUR_PUBLIC_KEY@localhost:8480/1",
environment: "production",
});
Full Nuxt guide — complete the remaining configuration and send a test event.
SvelteKit
npm install @sentry/sveltekit
src/instrumentation.server.ts
import * as Sentry from "@sentry/sveltekit";
Sentry.init({
dsn: "http://YOUR_PUBLIC_KEY@localhost:8480/1",
environment: "production",
});
For SvelteKit 3, do not create svelte.config.js; pass existing configuration directly to sveltekit() in vite.config.ts, without the kit wrapper, and omit experimental.instrumentation because server instrumentation is enabled by default. Keep sentrySvelteKit before sveltekit() and merge config into existing files.
Full SvelteKit guide — complete the remaining configuration and send a test event.
Structured logs, over the same DSN
Okapi collects structured logs over the same DSN as errors — no separate shipper. Enable your SDK's logging integration and logs flow in automatically. For example, in Laravel: set the Okapi DSN, SENTRY_ENABLE_LOGS=true, add a sentry_logs channel to LOG_STACK, then use the Log facade as usual. See Issues & logs for how logs are viewed and searched.
Source map uploads
Okapi symbolicates JavaScript/TypeScript stack traces from source maps you upload at deploy time, matched by Debug ID. It speaks Sentry's Debug ID artifact-bundle upload protocol. Standalone sentry-cli 2.58.6 has been validated against it (sourcemaps inject then sourcemaps upload, with and without --release).
Current sentry-cli releases negotiate chunked uploads only when the server advertises the release_files upload capability. Okapi advertises it. sentry-cli sends the chunks to the upload URL that Okapi builds from OKAPI_BASE_URL, so the machine that runs the upload (your CI runner, for example) must be able to reach that URL.
1. Create an upload token. Project → Settings → Source maps → New token. Copy it once (only a hash is stored) — tokens are scoped to one project.
2. Inject Debug IDs and upload with sentry-cli:
export SENTRY_URL=https://okapi.example.com # your Okapi base URL
export SENTRY_AUTH_TOKEN=okapi_xxxxxxxx… # the upload token
export SENTRY_ORG=okapi.example.com # your Okapi host; keeps the CLI org cache per instance
export SENTRY_PROJECT=your-project-slug
export SENTRY_CLI_NO_TELEMETRY=1
sentry-cli sourcemaps inject ./dist # stamp Debug IDs into bundle + maps
sentry-cli sourcemaps upload ./dist # upload to Okapi
--org is accepted but ignored — the token already scopes the upload to its project.
3. Upload during the production build. The current Next.js, Nuxt and SvelteKit guides upload to Okapi by default:
- Next.js: pass the upload options to
withSentryConfigfrom@sentry/nextjs/config. - Nuxt: put the upload options in
sentryand setsourcemap: { client: "hidden", server: "hidden" }andsentry.sourcemaps.filesToDeleteAfterUpload: [".output/public/**/*.map"]. The deletion glob assumes the default Node server preset (.output/public). On other Nitro presets, point it at that preset's public directory, for example.vercel/output/static/**/*.mapon Vercel ordist/**/*.mapon Netlify. - SvelteKit: pass the upload options and
autoUploadSourceMaps: truetosentrySvelteKitfrom@sentry/sveltekit/vite.
For Next.js, set sourcemaps.deleteSourcemapsAfterUpload: true explicitly. If productionBrowserSourceMaps is already set, keep it true and retain the explicit sourcemaps.deleteSourcemapsAfterUpload option; false prevents client map generation.
For SvelteKit, set sourcemaps.filesToDeleteAfterUpload: ["./.*/**/*.map", "./build/**/*.map"] explicitly. If build.sourcemap is already set, use "hidden" and retain the explicit deletion globs. These are the SDK's own default globs, which it drops once build.sourcemap is set: ./.*/** covers hidden adapter output such as .svelte-kit, .vercel, .netlify and .svelte-kit/cloudflare (adapter-auto, the default, picks one of these), and ./build/** covers adapter-node and adapter-static. If your adapter writes elsewhere, add a glob for its output folder.
Use these upload options with your framework:
process.env.SENTRY_CLI_NO_TELEMETRY ||= "1";
const uploadOptions = {
sentryUrl: "https://<your-okapi-host>",
org: "<your-okapi-host>",
project: "<project-slug>",
authToken: process.env.SENTRY_AUTH_TOKEN,
telemetry: false,
};
Set sentryUrl to the instance's exact OKAPI_BASE_URL. A fresh CLI refuses uploads to a different host, but cached regions per org are honoured. Use this instance's host as org to isolate its CLI region cache; dotted hosts cannot collide with sentry.io slugs.
Set SENTRY_AUTH_TOKEN to the project's Okapi upload token in the build environment.
When migrating from Sentry, replace Sentry's SENTRY_AUTH_TOKEN with an Okapi upload token and delete any leftover .env.sentry-build-plugin file (the plugin loads it into process.env).
The config defaults the CLI telemetry opt-out when unset. Set SENTRY_CLI_NO_TELEMETRY=1 in the build environment too. The plugin's telemetry: false option does not disable CLI telemetry.
Without a token, the build succeeds with warnings and skips release creation and uploads.
Set org to the host of the instance URL (including a custom port, if any), for example okapi.example.com. The plugin requires org, but Okapi resolves the project from the token.
For Vite and webpack, use url instead of sentryUrl in the raw plugin options.
Vite also requires build.sourcemap: "hidden" and sourcemaps.filesToDeleteAfterUpload: ["./dist/**/*.map"]. Change ./dist if build.outDir is customised.
For webpack, merge this configuration and replace ./dist with your output directory:
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");
process.env.SENTRY_CLI_NO_TELEMETRY ||= "1";
module.exports = {
mode: "production",
devtool: "hidden-source-map",
plugins: [sentryWebpackPlugin({
url: "https://<your-okapi-host>",
org: "<your-okapi-host>",
project: "<project-slug>",
authToken: process.env.SENTRY_AUTH_TOKEN,
telemetry: false,
sourcemaps: { filesToDeleteAfterUpload: ["./dist/**/*.map"] },
})],
};
The Browser JavaScript and Vue guides include an optional Vite example.
Standalone sentry-cli in step 2 remains an upload option.
Production builds validated against Okapi on 2026-11-04 uploaded Debug ID maps and produced symbolicated Chromium errors with source context:
| Platform | Versions |
|---|---|
| Vite | 8.3.5, @sentry/vite-plugin 11.10.0 |
| webpack | 5.112.2, @sentry/webpack-plugin 11.10.0 |
| Next.js (Turbopack) | 16.4.4, @sentry/nextjs 11.10.0 |
| Nuxt | 4.6.4, @sentry/nuxt 11.10.0 |
| SvelteKit | 3.1.1, @sentry/sveltekit 11.10.0, Vite 8.3.5 |
These plugins used JavaScript sentry CLI 0.45.0. Release creation and finalization succeeded against Okapi.
Next.js used native Turbopack Debug IDs. Next.js, Nuxt and SvelteKit also uploaded server maps.
All five no-token builds succeeded. Those runs explicitly set SENTRY_CLI_NO_TELEMETRY=1 and recorded no sentry.io DNS queries.
Follow-up Vite, webpack, Nuxt and Next.js upload builds started with both CLI opt-out variables unset.
Their configurations set the default with ||= and the DNS logger recorded no sentry.io lookups.
GET /api/0/organizations/{org}/ answers token-authenticated JSON for the new CLI.
Upload before events arrive. Okapi symbolicates before grouping, so an issue's fingerprint is based on the original (stable) frames. Upload maps at deploy time, before the new build starts sending events — events that arrive before their map is uploaded group on the minified frames and won't be re-grouped later.
Storage and retention. Maps are stored in Postgres (gzip-compressed), keyed by Debug ID, and listed under Settings → Source maps where you can delete them. Upload staging chunks are transient and garbage-collected hourly. Source maps are pruned automatically once they haven't symbolicated any event within the event-retention window (OKAPI_EVENT_RETENTION_DAYS); maps still in active use are kept.
Scope. Browser JS/TS (and compile-to-JS) and Node server stack traces with matching uploaded Debug ID maps are supported.
A Next.js 16.4.4 / @sentry/nextjs 11.10.0 route handler error resolved to the original app/api/…/route.js line with source context.
Node internals and server files without matching uploaded maps remain unresolved. Mobile/native symbolication (dSYM, ProGuard/R8, PDB, minidumps) and the legacy release+URL matching are not supported.
Next: SDK context.