# Noctant Private-Beta Self-Onboarding Guide

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

This is the one guide to give to a trusted private-beta app team or coding AI.
It contains the complete external path for connecting an app to Noctant through
the Operator API.

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.

## What "Self-Onboarding" Means Today

Private-beta self-onboarding means:

1. Noctant admin creates and activates the operator once.
2. Noctant admin gives the app its environment values once.
3. The app verifies its own setup with `GET /v1/me`.
4. The app runs the API smoke flow.
5. The app proves its webhook receiver.
6. The app opens a hosted launch and proves the user/product flow.
7. The app stores the proof and can keep iterating without Noctant internals.

This is not random public signup yet. A random internet company must not be able
to mint Operator API keys without review, rate-limit policy, webhook policy,
product grants, abuse controls, and support ownership.

## URLs

Staging:

- 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`
- 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.

## What Noctant Provides

For each app/environment, Noctant provides:

```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 that Noctant 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, Equity Log staging uses
  `https://staging.equitylog.app/api/webhooks/noctant/operator`.
- `NOCTANT_EXECUTION_PROFILE_KEY` is the trading execution profile for accounts,
  not the Operator API preset name. For the current Equity Log staging setup the
  account profile is `standard-netting-100`; `journal_account_creator` is the
  admin preset that grants scopes and webhook events.

## 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.

Every write request must include:

```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.

Account projections can become visible shortly after account creation. The
smoke helper retries only `GET /v1/accounts/by-external-id/summary` `404`
readback for a short bounded window; it does not hide `400`, `401`, `403`,
`409`, or `5xx` failures.

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`.

## Maintained SDK Smoke

The Noctant repo contains a maintained TypeScript smoke helper:

```text
packages/operator-api-typescript-sdk/examples/private-beta-self-onboarding.ts
```

It proves the API onboarding path against the public Operator API surface:

- verifies operator context;
- creates the test user;
- creates the sponsored journal account;
- creates a hosted launch session;
- redacts the launch token from evidence;
- reads back summary/orders/positions/trades;
- reads the webhook delivery ledger;
- requests bounded replay twice with the same idempotency key;
- returns a sanitized report.

The report contains object ids, counts, and the redacted launch URL only. It
does not contain the API key, webhook signing secret, or launch token.

This maintained smoke is intentionally server-side. It proves the account-first
private-beta API path. It does not replace a browser claim smoke for first-time
users, because claimable launches require Noctant browser login and explicit
consent before the account/session authority is materialized.

Example usage inside the target app:

```ts
import {
  configureSelfOnboardingFromEnvironment,
  runPrivateBetaSelfOnboardingSmoke,
} from "@noctant/operator-api-typescript-sdk/examples/private-beta-self-onboarding";

const input = configureSelfOnboardingFromEnvironment(process.env);
const report = await runPrivateBetaSelfOnboardingSmoke(input);

console.log(JSON.stringify(report, null, 2));
```

Until the SDK is published as a public npm package, an app can either vendor
the example, generate its own client from the OpenAPI JSON, or implement the
same requests with raw HTTPS.

## 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.
- 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.

Identity rule: the operator email helps the UI, but Noctant must not merge users
by email. If the browser is already signed in as a Noctant auth identity linked
to a different `global_user_id`, hosted launch must fail safely instead of
silently connecting the accounts. The target smooth flow is documented in
`docs/operator-api/2026-06-30-operator-launch-identity-claim-flow.md`: preview
the launch, ask for explicit consent when the operator identity is unclaimed,
and only require support/manual resolution for real conflicts.

## 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 header event id/type match the body envelope.
5. Store event ids durably.
6. Treat duplicate event ids as duplicate success.
7. Reject the same event id if the stored facts differ.

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.

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

## Activation Checklist

Noctant side:

- 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:

- create or replay stable operator users;
- 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.

It still cannot:

- create its own Operator API key without Noctant admin;
- change its webhook URL without Noctant admin or a future self-serve admin
  surface;
- 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, reconcile with account summary/orders/positions/trades
Receive signed webhooks
Use GET /v1/webhooks/deliveries and POST /v1/webhooks/replays for bounded
support/backfill

Every write must include x-noctant-request-id.

Do not use POST /v1/users for normal first-time onboarding. That endpoint is
privileged Noctant-admin/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.
Store event ids durably and process duplicates idempotently.

Use https://docs-staging.noctant.com/openapi/operator-api.json as the contract.
```

## Source References

- OpenAPI: `docs/api/operator-api.openapi.json`
- SDK package: `packages/operator-api-typescript-sdk`
- Self-onboarding SDK smoke:
  `packages/operator-api-typescript-sdk/examples/private-beta-self-onboarding.ts`
- Journal account flow:
  `packages/operator-api-typescript-sdk/examples/private-beta-journal-account-creator.ts`
- Historical launch flow:
  `packages/operator-api-typescript-sdk/examples/private-beta-historical-launch.ts`
- Internal admin runbook:
  `docs/operator-api/private-beta-internal-onboarding-runbook.md`
