name: ndb2-api description: >- Describes how mission-control integrates with the Off-Nominal NDB2 HTTP API and webhooks: V2 (preferred) vs legacy V1 paths, shared client, error shapes, and webhook routes. Use when adding or changing NDB2 calls, webhooks, predictions, bets, votes, scores, or migrating from V1 to V2.
NDB2 API (mission-control)
NDB2 is the predictions backend. This repo calls it over HTTP and receives webhooks. Prefer V2 for new work; V1 remains for endpoints not yet moved.
Configuration
mcconfig.ndb2.baseUrl— API origin (NDB2_API_BASEURL).mcconfig.ndb2.clientId— Bearer token for API and webhook auth (NDB2_CLIENT_ID).
All outbound calls use Authorization: Bearer <clientId>.
Types package
Shared contracts live in @offnominal/ndb2-api-types (see package.json version).
- V2: import from
@offnominal/ndb2-api-types/v2(e.g.Entities,Endpoints,Webhooks,Utils). - V1: local types in
src/providers/ndb2-client/types.tsunder namespaceNDB2API(and enums likePredictionLifeCycle).
HTTP client
src/providers/ndb2-client/index.ts — class Ndb2Client, default export ndb2Client (constructed with mcconfig.ndb2.clientId, initialize() loads seasons from V2).
Path convention: new URL(baseURL) then url.pathname = "api/v2/..." or "api/..." (V1).
V2 endpoints (use these when available)
| Method | Path | Client method | Notes |
|---|---|---|---|
| GET | api/v2/seasons | getSeasons() | Cached on client after initialize() |
| GET | api/v2/predictions/:id | getPrediction(id) | Returns data only |
| POST | api/v2/predictions | addPrediction(body) | Body type: Endpoints.Predictions.POST_Predictions.Body |
| PATCH | api/v2/predictions/:id/retire | retirePrediction(id, discord_id) | V2 retire |
Successful V2 JSON responses are unwrapped to res.data.data in the client. Errors use handleError: response shape { success, errors: [{ code, message }], data }; thrown value is a [userMessage, detailMessage] tuple (same pattern as other providers).
V1 endpoints (legacy; still in use)
Paths are under api/... (no v2). These methods use handleError_v1, which expects NDB2API.GeneralResponse: { success, errorCode, message, data }.
Still routed through Ndb2Client today:
POST api/predictions/:id/bets—addBetPOST api/predictions/:id/votes—addVotePOST api/predictions/:id/snooze_checks/:snoozeCheckId—addSnoozeVotePOST api/predictions/:id/trigger—triggerPredictionGET api/users/discord_id/:discord_id/scores—getScoresGET api/predictions/search—searchPredictions(SearchOptions,SortByOption)GET api/scores(withview=points|predictions|bets, optional season) — leaderboard helpersPATCH api/predictions/:id/snooze—snoozePrediction
When migrating a call from V1 to V2, switch the path, response unwrapping (data vs full body), error handler (handleError vs handleError_v1), and types to the V2 package.
Example: creating a prediction (V2)
Service pattern: src/services/ndb2/add-prediction/index.ts builds NDB2API.Endpoints.Predictions.POST_Predictions.Body with discord_id, text, date (ISO string), driver ("date" | "event"), then ndb2Client.addPrediction(body).
Webhooks
Router: src/services/ndb2/webhooks/index.ts.
- Auth:
validateWebhookAuthorization—Authorization: Bearermust matchmcconfig.ndb2.clientId. - V1 route:
POST .../ndb2— body must passisNdb2WebhookEvent(src/services/ndb2/webhooks/v1/types.ts). Several event names are short-circuited as ignored beforehandleV1Webhook(see router): e.g.new_prediction,untriggered_prediction,unjudged_prediction,retired_predictionon this path. - V2 route:
POST .../ndb2/v2— validate payload withAPI.Webhooks.isWebhookPayloadV2from@offnominal/ndb2-api-types/v2. Handler:src/services/ndb2/webhooks/v2/index.ts(e.g.untriggered_prediction,unjudged_prediction,retired_prediction).
Middleware responds early with JSON "thank u"; do not assume the handler must send the HTTP response.
Where to look in the tree
- Client + V1/V2 split:
src/providers/ndb2-client/ - Webhooks:
src/services/ndb2/webhooks/(v1/,v2/,middleware.ts) - Feature services:
src/services/ndb2/(e.g.add-prediction,retire-prediction)
When the OpenAPI or ndb2-api-types package changes, update call sites and this skill if behavior or paths shift.