# Noctant Private-Beta Self-Onboarding Guide

Date: 2026-06-29
Status: shareable private-beta onboarding guide

Use this guide for the private-beta onboarding ceremony. The journal app guide
(`https://docs-staging.noctant.com/operator-api/journal-app`) is the
integration contract.

It is intentionally limited to public contracts: HTTPS API, generated OpenAPI,
hosted launch URLs, signed webhooks, REST reconciliation, and bounded replay.
It does not require access to Noctant databases, NATS, Docker networks, internal
services, or source code.

## How Your Firm Gets In

Noctant's staff make each firm; there is no public firm sign-up. Nobody sends you a key or a secret: your firm's
own admin makes them in the console.

1. Sign up for a Noctant login at `https://app-staging.noctant.com` with the address your firm's first admin will
   use. Noctant mails that address a link; open it to prove the address.
2. Ask Noctant to make your firm, naming that address as its first admin. A login that is already staff of
   another firm cannot be named.
3. Sign in to the console at `https://admin-staging.noctant.com` with the same login. Your first sign-in with the
   proven address makes that login your firm's admin. If the mail never came or its link expired, sign in to the
   console anyway: it offers to send a new link.
4. In the console, create your operator (one per environment), apply its preset and set its allowed origins.
5. Provision the operator's API key and webhook signing secret. The console shows them once: store them as
   server-side secrets.
6. Set the operator's webhook URL.

Then your app verifies its setup with `GET /v1/me`, runs the API smoke flow, proves its webhook receiver, opens
a hosted launch and stores the proof, as the sections below describe.

## URLs

Staging:

- Sign-up: `https://app-staging.noctant.com`
- Console: `https://admin-staging.noctant.com`
- Operator API: `https://api-staging.noctant.com`
- Docs: `https://docs-staging.noctant.com`
- This guide: `https://docs-staging.noctant.com/operator-api/self-onboarding`
- Journal app guide: `https://docs-staging.noctant.com/operator-api/journal-app`
- Partner guide: `https://docs-staging.noctant.com/operator-api/partner-guide`
- Partner changelog: `https://docs-staging.noctant.com/operator-api/changelog`
- OpenAPI JSON: `https://docs-staging.noctant.com/openapi/operator-api.json`

The OpenAPI JSON is the machine-readable source of truth. Generate a client
from it or call the API directly with plain HTTPS.

## Environment Values

Configure these values for each app/environment. The journal app guide's
**Operator Setup And Credentials** section covers provisioning and
configuration.

```sh
NOCTANT_OPERATOR_API_BASE_URL=https://api-staging.noctant.com
NOCTANT_OPERATOR_API_KEY=...
NOCTANT_OPERATOR_ID=...
NOCTANT_OPERATOR_ORG_ID=...
NOCTANT_WEBHOOK_SIGNING_SECRET=...
NOCTANT_EXECUTION_PROFILE_KEY=...
NOCTANT_RETURN_URL=https://your-app.example/noctant/return
NOCTANT_WEBHOOK_URL=https://your-app.example/webhooks/noctant/operator
```

Rules:

- Store these values as server-side secrets.
- Never put the API key or webhook signing secret in browser code.
- Never log API keys, webhook secrets, cookies, authorization headers, or raw
  launch tokens.
- `NOCTANT_RETURN_URL` must use an origin allowlisted for this
  operator.
- `NOCTANT_WEBHOOK_URL` must be an HTTPS endpoint controlled by the operator.
- Use an exact webhook URL. Do not add a trailing `?`, fragment, or unused
  query string. For example:
  `https://journal-staging.example.com/api/webhooks/noctant/operator`.
- `NOCTANT_EXECUTION_PROFILE_KEY` is the trading execution profile for accounts,
  not the Operator API preset name. Use the profile configured for your firm;
  `trading_only` is a preset, a starting set of scopes.

## Required First Check

Before creating users or accounts, call:

```http
GET /v1/me
Authorization: Bearer <NOCTANT_OPERATOR_API_KEY>
```

The app must verify:

- `operator_id` equals `NOCTANT_OPERATOR_ID`;
- `org_id` equals `NOCTANT_OPERATOR_ORG_ID`;
- environment is the expected one;
- status is `active`;
- access is `allowlisted_beta`;
- webhook is configured and enabled;
- allowed origins include the origin of `NOCTANT_RETURN_URL`;
- scopes include the actions the app will use.

For the first journal/account-creator claimable launch smoke, the required
scopes are:

- `accounts:create`
- `launch_sessions:create`

If this check fails, stop and fix onboarding. Do not continue with partial
assumptions.

## API Smoke Flow

The app should run this flow with unique but stable test ids:

```text
external_user_id    = <app-prefix>-user-<run-id>
external_account_id = <app-prefix>-account-<run-id>
```

Use those ids for retries. Do not change ids when retrying the same logical
object.

Flow:

1. `GET /v1/me`
2. `POST /v1/claimable-launch-sessions`
3. Redirect the browser to the returned `launch_url`.
4. User signs in or signs up in Noctant.
5. User consents to connect the operator identity.
6. Noctant atomically creates/links the user/account/session and opens the
   requested route.
7. After the claim succeeds, use account readback and webhook replay endpoints
   for reconciliation.

Send this header with write requests:

```http
x-noctant-request-id: <uuid>
```

The request id is for logs and audit correlation. It is not the same as
endpoint idempotency.

The replay request must include an idempotency key. Repeating the exact same
body with the exact same key must be safe. Reusing the same key with different
facts is a caller bug.

Webhook replay windows must be fully in the past. `to_created_at` must not be
later than the server's current time, so smoke tests should use a small past
buffer rather than asking for `now + 5 minutes`.

## Hosted Launch Proof

`POST /v1/launch-sessions` returns a short-lived, one-time launch URL for an
already-linked user/account path.

`POST /v1/claimable-launch-sessions` returns a short-lived, one-time launch URL
for first-time user linking. In that flow, Noctant previews the operator
identity, waits for browser login/consent, binds `(operator_id,
external_user_id)` to the browser-selected Noctant `global_user_id`, then
creates the account and consumes the token in one transaction.

Rules:

- Create it only when the browser is ready to redirect.
- Redirect to it whole. The token is the URL fragment
  (`https://app-staging.noctant.com/launch#nlt_...`), the one part of a URL a
  browser sends to no server; a redirect that drops or encodes the `#` produces
  a link Noctant refuses.
- Do not store the plaintext token in durable logs.
- Do not auto-merge by email. Operator email/display name are only UI hints.
- In proof reports, redact it as:

```text
https://app-staging.noctant.com/launch#<redacted>
```

The user should land in Noctant on the expected route and account. If browser
launch fails, collect sanitized evidence: status code, request id, operator id,
external account id, launch session id, and timestamp. Do not collect cookies
or tokens.

## Webhook Receiver Proof

The app must implement an HTTPS webhook receiver before the integration is
trusted.

For every webhook:

1. Read the raw body bytes before JSON parsing.
2. Verify `X-Noctant-Signature` with `NOCTANT_WEBHOOK_SIGNING_SECRET`.
3. Reject stale timestamps.
4. Verify the event belongs to your org and its header event id/type match the
   body envelope.
5. Store event ids durably; deduplicate by event id only.
6. Answer `2xx` to a duplicate event id, whatever its body, and keep the body
   you stored first: every send carries a new signature, and a repeat of an
   event first delivered before Noctant began sending the `data` it recorded
   (2026-10-30) can differ in its body.
7. Answer `2xx` to a signed, in-org,
   [well-formed](2026-06-29-external-journal-app-integration-guide.md#webhooks)
   event you do not handle (a type, a value or a field your build does not
   know); store or ignore it. Refuse with `4xx` only a bad signature, another
   org's event or an envelope that is not well-formed.

The journal app guide's **Webhooks** section covers the envelope, handled and
unhandled events, delivery outcomes, and the subscription reported by
`GET /v1/me`.

Headers:

```http
X-Noctant-Event-Id: ...
X-Noctant-Event-Type: ...
X-Noctant-Signature: t=<unix_seconds>,v1=<hex-hmac-sha256>
```

The HMAC input is:

```text
<unix_seconds>.<raw body bytes>
```

For the first journal flow, expect account/trading events such as
`account.created` and `trade.closed`, depending on the operator's enabled event
filter.

## Reconciliation Proof

Webhooks are not enough. The app must also reconcile through REST:

- read account summary;
- read orders;
- read positions;
- read closed trades;
- compare stored webhook effects with REST readback;
- use bounded replay if a webhook was missed.

This is what makes redeploys, receiver downtime, retries, and webhook delivery
delays recoverable.

## Historical Replay And Manual Backtest

Do not use account-bound launch sessions for replay/backtest.

Historical replay/backtest uses:

```http
POST /v1/historical-launch-sessions
```

That route is controlled by explicit feed, time-window, product-mode, and
operator grants. It intentionally does not fake a live account. This prevents
analytics, trades, and account state from being mixed between live-style
accounts and historical simulation.

The journal app guide's **Historical Replay And Manual Backtest** section
covers launch times, the playback/backtest distinction, and session lifecycle.

Use the journal/account-creator smoke first. Only add historical replay or
manual backtest after the normal operator setup is proven.

## Activation Checklist

Firm staff:

- operator row exists;
- operator status is `active`;
- operator access is `allowlisted_beta`;
- API key is provisioned;
- webhook signing secret is provisioned;
- webhook URL is configured;
- allowed origins include the app's return URL origin;
- operator preset/scopes are correct;
- execution profile key is enabled;
- rate limit tier is intentional;
- readiness screen passes;
- sanitized smoke proof is attached.

App side:

- secrets are server-side only;
- `GET /v1/me` check passes;
- API smoke report passes;
- webhook signature verification passes;
- event id storage is idempotent;
- REST reconciliation works;
- bounded replay works;
- browser hosted launch lands on the right route/account;
- a test trade can be reconciled by webhook and REST readback.

## What The Other App Can Do After This Passes

With this onboarding path, a trusted private-beta app can:

- link operator users through Noctant browser login and consent;
- create or replay sponsored simulated journal accounts;
- send users into hosted Noctant terminal sessions;
- launch first-time users through Noctant-side login/consent with
  `POST /v1/claimable-launch-sessions`;
- receive signed account/trading webhooks;
- reconcile account state through REST;
- request bounded webhook replay;
- iterate on its own app without touching Noctant internals.

For key provisioning, webhook URL changes, and key rotation, see the journal
app guide's **Operator Setup And Credentials** section.

It still cannot:

- access accounts sponsored by other operators;
- access arbitrary historical feeds/time windows without explicit grants;
- rely on SDK-only behavior that is not present in OpenAPI.

## Copy-Paste Prompt For Another Coding AI

```text
Integrate this app with Noctant as a trusted private-beta operator.

Use only the public Noctant Operator API at NOCTANT_OPERATOR_API_BASE_URL.
Use Authorization: Bearer NOCTANT_OPERATOR_API_KEY for REST calls.
Do not use Noctant databases, NATS, Docker networks, or source internals.

First call GET /v1/me and verify operator id, org id, status, access,
environment, allowed origins, webhook config, and required scopes.

Then run the onboarding smoke:
POST /v1/claimable-launch-sessions
Redirect the browser to launch_url
Let the user sign in/sign up and consent inside Noctant
After claim, look the account up once with
GET /v1/accounts/by-external-id?external_account_id=..., keep its
program_account_id, and read GET /v1/accounts/{program_account_id}/summary,
/orders, /positions and /trades
Receive signed webhooks
Use GET /v1/webhooks/deliveries and POST /v1/webhooks/replays for bounded
support/backfill

Send x-noctant-request-id with writes.

Do not use POST /v1/users for normal first-time onboarding. That endpoint is
privileged import tooling and requires users:direct_provision.
Claimable launch is the smooth and secure path because Noctant binds the
operator identity only after browser login and consent.
Never log API keys, webhook secrets, cookies, authorization headers, or raw
launch tokens. Redact launch URLs as /launch#<redacted>.

Implement webhook signature verification using X-Noctant-Signature over
"<timestamp>.<raw body bytes>" with NOCTANT_WEBHOOK_SIGNING_SECRET.
The signed envelope is id, type, schema_version, created, data; verify that
data.org_id is your org. Deduplicate by X-Noctant-Event-Id in durable storage.
Answer 2xx to a verified event your build cannot read (a type, a value or a
field it does not know); refuse only a bad signature, another org's event or a
broken envelope.

Use https://docs-staging.noctant.com/openapi/operator-api.json as the contract.
Read https://docs-staging.noctant.com/operator-api/changelog before building
and before each release: its upgrade notes list every change an integration
written against an older call must make.
```

## Source References

- OpenAPI: `https://docs-staging.noctant.com/openapi/operator-api.json`
- Operator setup: your firm's admin creates the operator and its credentials in the console
  (**How Your Firm Gets In**); see the journal app guide's **Operator Setup And Credentials** section.
