@@ -330,6 +330,101 @@ export interface Event {
330330 status : 'upcoming' | 'live' | 'ended'
331331}
332332
333+ /** Subscription tier identifier */
334+ export type SubscriptionTier = 'free' | 'hobby' | 'pro' | 'sharp' | 'enterprise'
335+
336+ /** Single closing-line odd entry returned by `GET /odds/closing`.
337+ *
338+ * Field names match the wire format (snake_case). `line` only appears on
339+ * spread/total markets; `player_name` and `stat_category` only appear on
340+ * player-prop markets. */
341+ export interface ClosingOdd {
342+ sportsbook : string
343+ market_type : string
344+ selection : string
345+ selection_type :
346+ | 'home'
347+ | 'away'
348+ | 'over'
349+ | 'under'
350+ | 'draw'
351+ | 'home_draw'
352+ | 'away_draw'
353+ | 'home_away'
354+ | ( string & { } )
355+ odds_american : number
356+ odds_decimal : number
357+ /** Spread / total line — present only on point-spread and totals markets. */
358+ line ?: number
359+ /** Player-prop only. */
360+ player_name ?: string
361+ /** Player-prop only. */
362+ stat_category ?: string
363+ }
364+
365+ /** Books-keyed map of closing odds — one entry per sportsbook. */
366+ export type ClosingBooks = Record < string , ClosingOdd [ ] >
367+
368+ /** Closing-line snapshot for a single event.
369+ *
370+ * Returned in the `data` field of `GET /odds/closing?event_id=...`. When
371+ * no closing snapshot has been captured yet, `books` is an empty object
372+ * and `sport`/`league`/team fields may be absent. */
373+ export interface ClosingSnapshot {
374+ event_id : string
375+ sport ?: string
376+ league ?: string
377+ home_team ?: string
378+ away_team ?: string
379+ event_start_time ?: string
380+ /** Server-side capture timestamp (ISO 8601). */
381+ captured_at ?: string
382+ books : ClosingBooks
383+ }
384+
385+ /** API key record returned by `GET /account/keys` (and the create/rotate
386+ * endpoints, which add a one-time-only `key` field on the new key). */
387+ export interface APIKey {
388+ id : string
389+ /** Last-8-char-suffix mask of the key id (e.g. `"...FrolK3uD"`). */
390+ id_masked : string
391+ name : string | null
392+ tier : SubscriptionTier | ( string & { } )
393+ is_active : boolean
394+ created_at : string
395+ updated_at : string
396+ }
397+
398+ /** Response from `POST /account/keys` — includes the plaintext `key`
399+ * value, which is shown only once. */
400+ export interface CreatedAPIKey {
401+ id : string
402+ /** The full plaintext API key (e.g. `sk_live_xxx`). Only returned on
403+ * creation — the server cannot show it again. */
404+ key : string
405+ name : string | null
406+ tier : SubscriptionTier | ( string & { } )
407+ }
408+
409+ /** Response from `POST /account/keys/{id}/rotate`. The new key value is
410+ * shown only once; the old key may either be revoked immediately or
411+ * expire after a grace period. */
412+ export interface RotatedAPIKey {
413+ new_key : CreatedAPIKey
414+ old_key : {
415+ id : string
416+ revoked : boolean
417+ expires_at : string | null
418+ }
419+ }
420+
421+ /** Response from `DELETE /account/keys/{id}`. */
422+ export interface RevokedAPIKey {
423+ deleted : boolean
424+ key_id : string
425+ message : string
426+ }
427+
333428/** Account/key info */
334429export interface AccountInfo {
335430 key : {
@@ -406,6 +501,13 @@ export interface MiddlesParams {
406501 offset ?: number
407502}
408503
504+ export interface ClosingParams {
505+ /** Restrict the response to a specific sportsbook (or list — CSV on the
506+ * wire). When omitted, every book that has a closing snapshot for the
507+ * event is returned. */
508+ sportsbook ?: string | string [ ]
509+ }
510+
409511export interface StreamParams {
410512 sportsbook ?: string | string [ ]
411513 add_sportsbook ?: string | string [ ]
@@ -465,7 +567,7 @@ class HttpClient {
465567 }
466568
467569 private async request < T > (
468- method : 'GET' | 'POST' ,
570+ method : 'GET' | 'POST' | 'DELETE' ,
469571 path : string ,
470572 body ?: unknown ,
471573 params ?: Record < string , unknown > ,
@@ -541,6 +643,10 @@ class HttpClient {
541643 return this . request < T > ( 'POST' , path , body , params )
542644 }
543645
646+ async delete < T > ( path : string , params ?: Record < string , unknown > ) : Promise < T > {
647+ return this . request < T > ( 'DELETE' , path , undefined , params )
648+ }
649+
544650 getStreamUrl ( path : string , params ?: Record < string , unknown > ) : string {
545651 const url = this . buildUrl ( path , { ...params , api_key : this . apiKey } )
546652 return url
@@ -1024,6 +1130,37 @@ class OddsResource {
10241130 async batch ( eventIds : string [ ] ) : Promise < APIResponse < NormalizedOdds [ ] > > {
10251131 return this . http . post ( '/api/v1/odds/batch' , { event_ids : eventIds } )
10261132 }
1133+
1134+ /**
1135+ * Get the closing-line snapshot for a single event.
1136+ *
1137+ * Returns the per-book odds captured at event kickoff (the moment the
1138+ * sharp market locked). Used for line-shopping post-mortems and CLV
1139+ * (closing-line value) calculations.
1140+ *
1141+ * When no closing snapshot has been captured yet, `data.books` is an
1142+ * empty object.
1143+ *
1144+ * @param eventId — canonical event id (e.g. `"evt_..."`).
1145+ * @param options — optional `sportsbook` filter (CSV on the wire).
1146+ *
1147+ * @example
1148+ * ```typescript
1149+ * const { data } = await api.odds.closing('evt_123')
1150+ * for (const [book, odds] of Object.entries(data.books)) {
1151+ * console.log(book, odds.length, 'markets')
1152+ * }
1153+ * ```
1154+ */
1155+ async closing (
1156+ eventId : string ,
1157+ options ?: ClosingParams ,
1158+ ) : Promise < APIResponse < ClosingSnapshot > > {
1159+ return this . http . get ( '/api/v1/odds/closing' , {
1160+ event_id : eventId ,
1161+ ...( options ?? { } ) ,
1162+ } as Record < string , unknown > )
1163+ }
10271164}
10281165
10291166class ArbitrageResource {
@@ -1094,6 +1231,43 @@ class AccountResource {
10941231 }
10951232}
10961233
1234+ /** CRUD for the caller's API keys, backed by `/api/v1/account/keys`.
1235+ *
1236+ * Returned key records use snake_case field names to match the wire
1237+ * format ({@link APIKey}). The plaintext key value is only ever returned
1238+ * by {@link KeysResource.create} and {@link KeysResource.rotate} — the
1239+ * server cannot show it again. */
1240+ class KeysResource {
1241+ constructor ( private http : HttpClient ) { }
1242+
1243+ /** List every API key on the caller's account. */
1244+ async list ( ) : Promise <
1245+ APIResponse < APIKey [ ] > & { meta ?: { count ?: number ; max_keys ?: number } }
1246+ > {
1247+ return this . http . get ( '/api/v1/account/keys' )
1248+ }
1249+
1250+ /** Create a new API key. The plaintext `key` value in the response is
1251+ * shown only once — the caller must store it securely. */
1252+ async create ( name : string ) : Promise < APIResponse < CreatedAPIKey > > {
1253+ return this . http . post ( '/api/v1/account/keys' , { name } )
1254+ }
1255+
1256+ /** Revoke an API key by id. The caller cannot revoke the key they are
1257+ * currently authenticating with. */
1258+ async revoke ( keyId : string ) : Promise < APIResponse < RevokedAPIKey > > {
1259+ return this . http . delete ( `/api/v1/account/keys/${ encodeURIComponent ( keyId ) } ` )
1260+ }
1261+
1262+ /** Rotate an API key — atomically issues a replacement and revokes the
1263+ * original. The new plaintext `key` is shown only once. */
1264+ async rotate ( keyId : string ) : Promise < APIResponse < RotatedAPIKey > > {
1265+ return this . http . post (
1266+ `/api/v1/account/keys/${ encodeURIComponent ( keyId ) } /rotate` ,
1267+ )
1268+ }
1269+ }
1270+
10971271class StreamResource {
10981272 private apiKey : string
10991273
@@ -1230,6 +1404,8 @@ export class SharpAPI {
12301404 readonly middles : MiddlesResource
12311405 /** Account endpoints */
12321406 readonly account : AccountResource
1407+ /** API key management — `/api/v1/account/keys` CRUD. */
1408+ readonly keys : KeysResource
12331409 /** Streaming endpoints (requires WebSocket add-on) */
12341410 readonly stream : StreamResource
12351411
@@ -1245,6 +1421,7 @@ export class SharpAPI {
12451421 this . ev = new EVResource ( this . http )
12461422 this . middles = new MiddlesResource ( this . http )
12471423 this . account = new AccountResource ( this . http )
1424+ this . keys = new KeysResource ( this . http )
12481425 this . stream = new StreamResource ( this . http )
12491426 }
12501427}
0 commit comments