puntersedge 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -133,12 +133,19 @@ export interface BackfillStatusOut {
133
133
  estimated_ready?: string | null;
134
134
  /** Completed rows you have not yet read. */
135
135
  ready_to_collect: number;
136
+ /**
137
+ * Whether Racing Australia is answering form reads at all. Added 2026-09-21 and purely
138
+ * additive: nothing else on this response, and nothing about billing or the queue, changed
139
+ * with it.
140
+ */
141
+ upstream: UpstreamBlock;
136
142
  }
137
143
 
138
144
  export interface BackfillSubmitIn {
139
145
  /**
140
- * Horse names or ra: refs. Max 500 per call. A ref is exact; a name is resolved against
141
- * acceptances and archived results.
146
+ * Horse names, `pe:` horse_refs or `ra:` entry codes, in any mix. Max 500 per call. A name and
147
+ * its horse_ref resolve identically, against acceptances and archived results; an `ra:` code
148
+ * pins one meeting's entry.
142
149
  */
143
150
  horses: string[];
144
151
  /** Your own tag for this batch, echoed back on status. */
@@ -151,8 +158,9 @@ export interface BackfillSubmitOut {
151
158
  /** Horses accepted and waiting on the worker. */
152
159
  queued: number;
153
160
  /**
154
- * Horses we already hold a fresh copy of. Collectable immediately, still billed at 3 credits
155
- * each.
161
+ * Horses we already hold a fresh copy of — matched on the HORSE since 2026-09-21, so a page
162
+ * collected under any of its Racing Australia entry codes counts. Collectable immediately,
163
+ * still billed at 3 credits each.
156
164
  */
157
165
  already_stored: number;
158
166
  /** Already queued for you; not queued twice. */
@@ -189,6 +197,43 @@ export interface BestPricesOut {
189
197
  selections: Record<string, unknown>[];
190
198
  }
191
199
 
200
+ export interface BulkCatalogueOut {
201
+ datasets: BulkDatasetOut[];
202
+ formats?: string[];
203
+ day_plans: string;
204
+ month_plans: string;
205
+ credits: Record<string, number>;
206
+ schedule: string;
207
+ notes?: string[];
208
+ }
209
+
210
+ export interface BulkDatasetOut {
211
+ name: string;
212
+ title: string;
213
+ description: string;
214
+ grain: string;
215
+ /** Column order of every file in this dataset, read from the newest file's sidecar. */
216
+ columns?: string[];
217
+ available_from: string;
218
+ /** Daily files, oldest first. Business and above. */
219
+ days?: BulkFileOut[];
220
+ /** Monthly bundles, oldest first. Platform and above. */
221
+ months?: BulkFileOut[];
222
+ }
223
+
224
+ export interface BulkFileOut {
225
+ period: string;
226
+ /** day (YYYY-MM-DD, Sydney meeting date) or month (YYYY-MM). */
227
+ kind: string;
228
+ rows?: number | null;
229
+ generated_at?: string | null;
230
+ /** Size per format: parquet, csv (gzip). */
231
+ bytes?: Record<string, number>;
232
+ /** Digest per format; also the ETag. */
233
+ sha256?: Record<string, string>;
234
+ url: string;
235
+ }
236
+
192
237
  export interface ChangedRaceOut {
193
238
  /**
194
239
  * Stable race identifier — the same id /v1/racing/next-to-go, /v1/racing/price-history and
@@ -321,6 +366,129 @@ export interface ClosingLinesOut {
321
366
  rows: Record<string, unknown>[];
322
367
  }
323
368
 
369
+ export interface ClvBetIn {
370
+ /** Your own id for this bet, echoed back unchanged. */
371
+ ref?: string | null;
372
+ /**
373
+ * The race_id from /v1/racing/next-to-go, /events, /results or the archive. The most reliable
374
+ * way to name a race; use venue + meeting_date + race_number only when you do not hold it.
375
+ */
376
+ race_id?: string | null;
377
+ /** Venue name as our feeds spell it (case-insensitive). Needs meeting_date and race_number. */
378
+ venue?: string | null;
379
+ race_number?: number | null;
380
+ /** AET meeting date, YYYY-MM-DD. */
381
+ meeting_date?: string | null;
382
+ /**
383
+ * Registry id as served on /results and the archive — ra:<horsecode> or grv:<dogId>. Exact
384
+ * match; the most reliable way to name a runner.
385
+ */
386
+ runner_ref?: string | null;
387
+ /** Saddlecloth number. */
388
+ runner_number?: number | null;
389
+ /**
390
+ * Runner name; matched the same way the feeds match names (case, punctuation and country
391
+ * suffixes ignored).
392
+ */
393
+ runner_name?: string | null;
394
+ /**
395
+ * bookmaker_key the bet was struck with. When given, `clv_pct` is measured against THAT book's
396
+ * closing price and `close_book` is filled; when absent, against the market reference chosen
397
+ * by `reference`.
398
+ */
399
+ bookmaker?: string | null;
400
+ /** Decimal odds you took. */
401
+ price: number;
402
+ /** Stake for the P&L line. Defaults to one flat unit. */
403
+ stake?: number;
404
+ }
405
+
406
+ export interface ClvBetOut {
407
+ ref?: string | null;
408
+ /**
409
+ * True when the race AND the runner were found in the archive. False rows carry `reason` and
410
+ * no numbers.
411
+ */
412
+ resolved: boolean;
413
+ /**
414
+ * race_not_found · race_ambiguous · runner_not_found · runner_ambiguous · bookmaker_withheld ·
415
+ * no_closing_line · no_closing_line_for_book.
416
+ */
417
+ reason?: string | null;
418
+ race_id?: string | null;
419
+ venue?: string | null;
420
+ race_number?: number | null;
421
+ runner_key?: string | null;
422
+ runner_name?: string | null;
423
+ runner_number?: number | null;
424
+ runner_ref?: string | null;
425
+ bookmaker?: string | null;
426
+ price_taken: number;
427
+ stake: number;
428
+ /** The named bookmaker's closing win price, when it recorded a closing line. */
429
+ close_book?: number | null;
430
+ /** Highest closing win price across books. */
431
+ close_best?: number | null;
432
+ /** Median closing win price across books. */
433
+ close_median?: number | null;
434
+ /** Books with a closing line for this runner. */
435
+ books_at_close?: number;
436
+ /**
437
+ * The close `clv_pct` is measured against: close_book when a bookmaker was named and priced
438
+ * the close, else the market reference.
439
+ */
440
+ close_reference?: number | null;
441
+ /** (price_taken / close_reference − 1) × 100. Positive means you beat the close. */
442
+ clv_pct?: number | null;
443
+ beat_close?: boolean | null;
444
+ finish_position?: number | null;
445
+ /** final · interim · null (not yet resulted). */
446
+ result_status?: string | null;
447
+ /** finish_position == 1 on a final result; null until final. */
448
+ won?: boolean | null;
449
+ /** stake × (price − 1) on a win, −stake on a loss, null until the result is final. */
450
+ pnl?: number | null;
451
+ }
452
+
453
+ export interface ClvIn {
454
+ /** Up to 200 bets per request. */
455
+ bets: ClvBetIn[];
456
+ /**
457
+ * The market close used when a bet names no bookmaker, and reported as close_best /
458
+ * close_median on every bet regardless: `best` is the highest closing win price across books,
459
+ * `median` the median.
460
+ */
461
+ reference?: string;
462
+ }
463
+
464
+ export interface ClvOut {
465
+ reference: string;
466
+ /** Earliest race day the archive holds; bets before it cannot resolve. */
467
+ archive_floor?: string | null;
468
+ summary: ClvSummaryOut;
469
+ bets: ClvBetOut[];
470
+ notes?: string[];
471
+ }
472
+
473
+ export interface ClvSummaryOut {
474
+ bets: number;
475
+ resolved: number;
476
+ unresolved: number;
477
+ /** Bets with a closing line to measure against. */
478
+ scored: number;
479
+ avg_clv_pct?: number | null;
480
+ median_clv_pct?: number | null;
481
+ beat_close?: number;
482
+ beat_close_rate_pct?: number | null;
483
+ /** Scored-or-resolved bets with a final result. */
484
+ resulted?: number;
485
+ wins?: number;
486
+ win_rate_pct?: number | null;
487
+ staked?: number | null;
488
+ pnl?: number | null;
489
+ roi_pct?: number | null;
490
+ }
491
+
324
492
  export interface ConnectorHealthOut {
325
493
  connector: string;
326
494
  last_ok: string | null;
@@ -330,106 +498,147 @@ export interface ConnectorHealthOut {
330
498
  message?: string | null;
331
499
  }
332
500
 
333
- export interface DogCandidate {
334
- dog_id: number;
335
- dog_name?: string | null;
336
- /** Career starts on this feed, scratchings included. */
337
- runs: number;
338
- /** Meeting date of the most recent run. */
339
- last_start?: string | null;
340
- }
341
-
342
501
  export interface GreyhoundFormOut {
343
- dog_id?: number | null;
344
- dog_name?: string | null;
345
- /** True when the name matched several dogs. `runs` is then empty and `candidates` lists them. */
346
- ambiguous: boolean;
347
- candidates?: DogCandidate[];
348
- runs?: GreyhoundRun[];
502
+ /** The dog's name as our results spell it. */
503
+ dog: string;
504
+ /** The identity the runs were grouped on (letters and digits). */
505
+ name_key: string;
506
+ /** Every registry id seen for this name, across both collectors. */
507
+ runner_refs: string[];
508
+ /** Runs held for this dog since `source.coverage_from`; `runs` is the newest `limit` of them. */
509
+ runs_total: number;
510
+ runs: GreyhoundRun[];
349
511
  source: SourceBlock;
350
512
  }
351
513
 
352
514
  export interface GreyhoundRun {
353
- /** Meeting date, AEST calendar day, YYYY-MM-DD. */
515
+ /** Sydney calendar date of the race. */
354
516
  date?: string | null;
355
- /** Official Topaz track name, sponsor included (e.g. 'Bet Deluxe Capalaba'). */
356
- track?: string | null;
357
- distance_m?: number | null;
358
- /** Topaz raceTypeCode, e.g. '5', 'M', 'X45'. */
359
- grade?: string | null;
517
+ /** Scheduled start, UTC. */
518
+ start_time?: string | null;
519
+ venue?: string | null;
360
520
  race_number?: number | null;
521
+ distance_m?: number | null;
522
+ race_name?: string | null;
523
+ track_condition?: string | null;
361
524
  box?: number | null;
362
- rug?: number | null;
363
- weight_kg?: number | null;
364
- /** Official starting price, decimal. */
365
- sp?: number | null;
366
- /** Finishing place. NULL when the dog did not complete the course or was scratched. */
525
+ /** Rug number; equals box for a starter. */
526
+ number?: number | null;
527
+ /**
528
+ * Finishing place. Null when the dog did not run (see `status`) or did not complete the
529
+ * course.
530
+ */
367
531
  position?: number | null;
368
- scratched: boolean;
369
- /** Fell, TailedOff, PulledUp, Disqualified or StayedInBox. NULL for a normal run. */
370
- abnormal?: string | null;
371
- time_s?: number | null;
372
- /** Winning time of this race, i.e. the place-1 dog's time. */
373
- win_time_s?: number | null;
374
- /** Margin to the winner in lengths, as Topaz publishes it (e.g. '5.50L'). */
532
+ /** `ran`, `scratched`, or `reserve` (a rug 9/10 reserve that never got a box). */
533
+ status?: string | null;
534
+ /**
535
+ * Lengths behind the winner as the source printed it; the winner's own row carries the winning
536
+ * margin on Victorian tracks and null elsewhere.
537
+ */
375
538
  margin?: string | null;
539
+ /** The dog's own finish time in seconds. */
540
+ time_s?: number | null;
541
+ /**
542
+ * First sectional in seconds, as the source page printed it. Null where it printed none — a
543
+ * reserve that never started has none, and some tracks publish no sectionals at all.
544
+ */
545
+ first_split_s?: number | null;
376
546
  /**
377
- * Margin to the winner in seconds. On the winner's own run this is the winning margin over the
378
- * runner-up.
547
+ * Second sectional in seconds. Null on every Victorian run: FastTrack publishes one split, not
548
+ * two.
379
549
  */
380
- margin_s?: number | null;
381
- first_split_time?: number | null;
382
- first_split_position?: number | null;
550
+ second_split_s?: number | null;
383
551
  /**
384
- * Second sectional split. Only some tracks time two points: 21% of all runs carry one (603,734
385
- * of 2,861,690, measured 2026-09-02).
552
+ * Race-day weight in kilograms. Null where the page prints no weight — Western Australian
553
+ * meetings carry no weight column at all.
386
554
  */
387
- second_split_time?: number | null;
388
- second_split_position?: number | null;
389
- /** The dog's grade entering this race, as Topaz graded it. */
390
- grade_in?: string | null;
555
+ weight_kg?: number | null;
391
556
  /**
392
- * The dog's grade after this race's result was applied. A value differing from grade_in is a
393
- * grade change — a win typically moves the dog down a number (5 -> 4).
557
+ * Position in running, FastTrack's own digits ("21" = 2nd then 1st at the marks), passed
558
+ * through as TEXT and not a number. Victorian runs only; null everywhere else.
394
559
  */
395
- grade_out?: string | null;
396
- /** Position in running, one digit per section. */
397
560
  pir?: string | null;
398
- trainer: TrainerRef;
399
- /** Topaz raceId. Every runner in the same race shares it. */
400
- topaz_race_id?: number | null;
401
- dog_id: number;
402
- dog_name?: string | null;
561
+ /**
562
+ * The race's grade in the SOURCE'S OWN WORDS — "M", "4/5", "FFA" on the national pages, a race
563
+ * type like "Tier 3 - Maiden" on FastTrack. The two vocabularies are NOT mapped onto each
564
+ * other and must not be compared across states. Null on runs whose field was stored before
565
+ * 2026-09-21.
566
+ */
567
+ grade?: string | null;
568
+ /** The winner's time in the same race, so `time_s - win_time_s` is the deficit. */
569
+ win_time_s?: number | null;
570
+ /** Official starting price, decimal return on $1. Null = unknown, never zero. */
571
+ sp?: number | null;
572
+ dead_heat?: boolean;
573
+ trainer?: string | null;
574
+ /** Dogs that ran in that race. */
575
+ field_size?: number | null;
576
+ /** Registry id of the dog as the collector for that track numbers it: grv:<id> or grsa:<id>. */
577
+ runner_ref?: string | null;
578
+ /** `fasttrack` or `grsa` — which collector wrote the race. */
579
+ source?: string | null;
580
+ /**
581
+ * `results` — the race is one we settled and serve on /v1/racing/results; `history` — the race
582
+ * was read back off the same official results page by our history collector and has no results
583
+ * row.
584
+ */
585
+ origin?: string | null;
586
+ /**
587
+ * Id of the /v1/racing/results row this run came from. On an `origin: history` run it is a
588
+ * synthetic `hist:…` id that joins to nothing.
589
+ */
590
+ result_id?: string | null;
403
591
  }
404
592
 
405
- export interface GreyhoundStatGroup {
593
+ export interface GreyhoundStatRow {
594
+ /** Set when `by` includes track. */
406
595
  track?: string | null;
596
+ /** Set when `by` includes distance. */
407
597
  distance_m?: number | null;
598
+ /** Set when `by` includes box. */
408
599
  box?: number | null;
409
- grade?: string | null;
600
+ /**
601
+ * Runs with status `ran` — scratchings and unused reserves are excluded from every figure
602
+ * here.
603
+ */
410
604
  starts: number;
411
605
  wins: number;
412
- /** Finishes in the first three. */
606
+ /** Finishes in the first three, wins included. */
413
607
  places: number;
414
608
  win_pct: number;
415
609
  place_pct: number;
610
+ /** Over the runs with a recorded time. */
416
611
  avg_time_s?: number | null;
417
612
  best_time_s?: number | null;
418
- avg_first_split?: number | null;
613
+ /**
614
+ * Mean first sectional over the runs in this group that carry one. Null when none does — the
615
+ * count behind it is not `starts`, because some tracks publish no sectionals.
616
+ */
617
+ avg_first_split_s?: number | null;
618
+ /** Mean official starting price over the runs that carry one. */
619
+ avg_sp?: number | null;
620
+ /** Sydney date of the most recent run in the group. */
621
+ last_start?: string | null;
419
622
  }
420
623
 
421
624
  export interface GreyhoundStatsOut {
422
- dog_id?: number | null;
423
- dog_name?: string | null;
424
- ambiguous: boolean;
425
- candidates?: DogCandidate[];
426
- group_by?: string[];
427
- groups?: GreyhoundStatGroup[];
625
+ dog: string;
626
+ name_key: string;
627
+ runner_refs: string[];
628
+ /** The dimensions the rows are grouped by, in the order given. */
629
+ by: string[];
630
+ /** One row per distinct combination, most starts first. */
631
+ rows: GreyhoundStatRow[];
632
+ /** The same figures over every run since `source.coverage_from`. */
633
+ overall: GreyhoundStatRow;
428
634
  source: SourceBlock;
429
635
  }
430
636
 
431
637
  export interface HorseCandidate {
432
- /** Stable ra: horsecode — re-query with this. */
638
+ /**
639
+ * Racing Australia entry code — re-query with this to pin one meeting's entry. `candidates` is
640
+ * empty from 2026-09-21: a name resolves to one horse. See `ambiguous`.
641
+ */
433
642
  runner_ref: string;
434
643
  runner_name?: string | null;
435
644
  /** Most recent date this ref appeared on our feeds (acceptance or result). */
@@ -460,11 +669,25 @@ export interface HorseCareer {
460
669
  export interface HorseFormOut {
461
670
  horse_name?: string | null;
462
671
  /**
463
- * Stable ra: horsecode — the same identifier on /v1/racing/results runners and
464
- * acceptance-enriched live runners.
672
+ * The HORSE: `pe:<name>`, the registered name folded to letters and digits. The same value on
673
+ * /v1/racing/results runners and placings, the live board, the closing-line and price-path
674
+ * archives, /v1/racing/horses/runs and the form backfill — and accepted as `horse` here. This
675
+ * is the key to join a horse across meetings.
676
+ */
677
+ horse_ref?: string | null;
678
+ /**
679
+ * Racing Australia's code for the ENTRY whose page was read — the same identifier on
680
+ * /v1/racing/results runners and acceptance-enriched live runners FOR THAT MEETING. It changes
681
+ * from meeting to meeting (measured 2026-09-21: 1,450 of 1,452 horses with two or more runs
682
+ * carried a different code on each), so it is not a horse identity — `horse_ref` is. Where a
683
+ * horse has several codes this is the most recently seen one.
465
684
  */
466
685
  runner_ref?: string | null;
467
- /** True when the name matched several horses. `runs` is then empty and `candidates` lists them. */
686
+ /**
687
+ * ALWAYS FALSE since 2026-09-21, and kept only so that callers reading it do not break. A name
688
+ * now resolves to one horse: several ra: codes under one name are the ordinary case (they are
689
+ * entry codes), not two horses. `candidates` is empty with it.
690
+ */
468
691
  ambiguous: boolean;
469
692
  candidates?: HorseCandidate[];
470
693
  profile?: HorseProfile | null;
@@ -543,6 +766,111 @@ export interface HorseRun {
543
766
  placegetters?: RunPlacegetter[];
544
767
  }
545
768
 
769
+ export interface HorseRunRow {
770
+ /** Sydney calendar date of the race. */
771
+ date?: string | null;
772
+ /** Scheduled start, UTC. */
773
+ start_time?: string | null;
774
+ venue?: string | null;
775
+ race_number?: number | null;
776
+ distance_m?: number | null;
777
+ race_name?: string | null;
778
+ /** As published for the meeting, e.g. 'Good 4'. */
779
+ track_condition?: string | null;
780
+ /** Saddlecloth number. */
781
+ number?: number | null;
782
+ /** Barrier. Null on a scratching — a runner that never jumped has no barrier. */
783
+ box?: number | null;
784
+ /**
785
+ * Finishing place. Null when the horse did not start (see `status`) or started and did not
786
+ * finish (see `abnormal`).
787
+ */
788
+ position?: number | null;
789
+ /** `ran` or `scratched`, exactly as /v1/racing/results publishes it. */
790
+ status?: string | null;
791
+ /**
792
+ * Racing Australia's own Finish cell whenever it is not a number, passed through verbatim
793
+ * (Fell, PulledUp, TailedOff, LR, FF, LP, and 'SB' for scratched at the barrier). THERE IS NO
794
+ * CLOSED LIST — an unseen code reaches you unchanged. Read it with `status`.
795
+ */
796
+ abnormal?: string | null;
797
+ /** Lengths behind the winner as RA printed it ('0.75L'); null where it prints none. */
798
+ margin?: string | null;
799
+ /**
800
+ * The horse's own race time in seconds. NULL on every row today: RA's results table publishes
801
+ * no per-runner time (0 of 10,092 stored runners carry one, measured 2026-09-21).
802
+ * /v1/racing/horses/form carries RA's official race time where we hold that horse's page.
803
+ */
804
+ time_s?: number | null;
805
+ /** Official starting price, decimal return on $1. Null = unknown, never zero. */
806
+ sp?: number | null;
807
+ /** RA's favouritism marker: 'F' favourite, 'EF' equal favourite; null otherwise. */
808
+ sp_note?: string | null;
809
+ dead_heat?: boolean;
810
+ /** Trainer of the day for THIS run, as the result published it. */
811
+ trainer?: string | null;
812
+ /** Rider of the day for THIS run. */
813
+ jockey?: string | null;
814
+ /** Horses that ran in that race. */
815
+ field_size?: number | null;
816
+ /**
817
+ * The Racing Australia horsecode our results collector published for THIS entry — the same
818
+ * value on the matching /v1/racing/results runner. It is not a horse identity here: measured
819
+ * 2026-09-21 over the whole store, 10,092 runner rows carry 9,872 distinct codes across 7,326
820
+ * names, so the same horse usually carries a different code at each meeting. Group on
821
+ * `horse_ref`, not on this.
822
+ */
823
+ runner_ref?: string | null;
824
+ /**
825
+ * The horse this run belongs to: `pe:<name>`, the registered name folded to letters and
826
+ * digits. Identical on every run of the same horse, and the same value /v1/racing/results
827
+ * runners and placings, the live board, the closing-line and price-path archives and
828
+ * /v1/racing/horses/form carry — the key that joins a horse across meetings, where
829
+ * `runner_ref` joins one entry to one meeting.
830
+ */
831
+ horse_ref?: string | null;
832
+ /** Id of the /v1/racing/results row this run came from. */
833
+ result_id?: string | null;
834
+ }
835
+
836
+ export interface HorseRunsOut {
837
+ /** The horse's name as our results spell it. */
838
+ horse: string;
839
+ /** The identity the runs were grouped on (letters and digits). */
840
+ name_key: string;
841
+ /**
842
+ * `name_key` as the identifier every thoroughbred surface publishes: `pe:<name_key>`. Pass it
843
+ * back here, or to /v1/racing/horses/form, and read it off /v1/racing/results runners and
844
+ * placings, the live board and the closing-line and price-path archives to gather one horse
845
+ * across meetings.
846
+ */
847
+ horse_ref?: string | null;
848
+ /**
849
+ * Every Racing Australia horsecode our results published under this name, sorted. Several is
850
+ * normal, not an error — see `runs[].runner_ref`.
851
+ */
852
+ runner_refs: string[];
853
+ /** Runs held for this horse since `source.coverage_from`; `runs` is the newest `limit` of them. */
854
+ runs_total: number;
855
+ runs: HorseRunRow[];
856
+ /** Over EVERY held run, not just the ones returned. */
857
+ stats: HorseRunsStats;
858
+ source: RunsSourceBlock;
859
+ }
860
+
861
+ export interface HorseRunsStats {
862
+ /**
863
+ * Runs with status `ran` — scratchings are excluded from every figure here, which is why
864
+ * `starts` can be lower than `runs_total`.
865
+ */
866
+ starts: number;
867
+ wins: number;
868
+ /** Finishes in the first three, wins included. */
869
+ places: number;
870
+ win_pct: number;
871
+ place_pct: number;
872
+ }
873
+
546
874
  export interface HorseSource {
547
875
  feed?: string;
548
876
  region?: string;
@@ -724,6 +1052,27 @@ export interface NextRaceOut {
724
1052
  cache_age_seconds?: number | null;
725
1053
  }
726
1054
 
1055
+ export interface OverageIn {
1056
+ /**
1057
+ * true lets this account run past its monthly allowance, up to the ceiling, and be invoiced
1058
+ * for the excess. false restores the hard 402 at the allowance.
1059
+ */
1060
+ enabled: boolean;
1061
+ }
1062
+
1063
+ export interface OverageOut {
1064
+ enabled: boolean;
1065
+ eligible: boolean;
1066
+ rate_per_1000_cents: number;
1067
+ max_credits: number;
1068
+ ceiling: number;
1069
+ credits_over_this_period: number;
1070
+ estimated_cents_this_period: number;
1071
+ plan: string;
1072
+ credits_limit: number;
1073
+ credits_used: number;
1074
+ }
1075
+
727
1076
  export interface PremiershipOut {
728
1077
  role: string;
729
1078
  season_start_year?: number | null;
@@ -977,28 +1326,35 @@ export interface RaceResultOut {
977
1326
  */
978
1327
  status_note?: string | null;
979
1328
  /**
980
- * The placegetters: {position, number, name, win_price, place_price, sp, sp_note}. `win_price`
981
- * and `place_price` are the SETTLING BOOKMAKER'S FIXED prices. `sp` is the OFFICIAL STARTING
982
- * PRICE as a decimal return on a $1 stake (4.6 means $4.60), null where it is unknown — never
983
- * 0. It is the settling market's own price, not our consensus and not a bookmaker's fixed
984
- * price: greyhound SP comes from the official Topaz feed, thoroughbred SP from the Racing
985
- * Australia results table, and HARNESS SP IS ALWAYS NULL — no source we hold publishes it.
986
- * `sp_note` carries Racing Australia's favouritism marker on thoroughbred runners ('F'
987
- * favourite, 'EF' equal favourite) and is null everywhere else. Coverage RE-MEASURED
988
- * 2026-09-08 over the whole retained store, counting only rows whose `status` is `ran` (a
989
- * barrier scratching is not a starter and its price is not in this denominator): 99.8% of
990
- * greyhound starters carry `sp` (14,540 of 14,568) and 88.6% of thoroughbred starters do
991
- * (2,105 of 2,375). The Racing Australia block that held thoroughbred SP at 0% when this was
992
- * last measured on 2026-09-03 has lifted and the collector has been landing meetings since. A
993
- * null `sp` is unknown, never zero — RA prints no SP at all on some rows. On placings, `sp` is
994
- * copied from the matching `runners` entry when the response is built, so the two can never
995
- * disagree. On a race with no `runners` payload the `sp` and `sp_note` KEYS ARE ABSENT from
996
- * each placing — not present and null. The copy step is what creates them and it is skipped
997
- * when there is nothing to copy from, so a schema-validating consumer sees a different object
998
- * shape, not a null: read them with `.get()`. The keys are deliberately NOT normalised in,
999
- * because adding them here would change the shipped payload of every COMPLETE race too, and
1000
- * nothing may move under a customer mid-integration. `field_status` tells you which case you
1001
- * are in before you look.
1329
+ * The placegetters: {position, number, name, win_price, place_price, sp, sp_note, horse_ref}.
1330
+ * `win_price` and `place_price` are the SETTLING BOOKMAKER'S FIXED prices. `horse_ref` (added
1331
+ * 2026-09-21) is the same `pe:<name>` horse identity the `runners` entries carry, derived at
1332
+ * serve time from the name on the placing and present on every placing — null off
1333
+ * thoroughbred. It is the key that joins this placing to the same horse on another meeting;
1334
+ * `runner_ref` on the matching `runners` entry is Racing Australia's per-MEETING entry code
1335
+ * and will not. `sp` is the OFFICIAL STARTING PRICE as a decimal return on a $1 stake (4.6
1336
+ * means $4.60), null where it is unknown — never 0. It is the settling market's own price, not
1337
+ * our consensus and not a bookmaker's fixed price: thoroughbred SP comes from the Racing
1338
+ * Australia results table, greyhound SP from our own collection of published results
1339
+ * (Victorian tracks via FastTrack, every other state via the national results pages, both from
1340
+ * 2026-09-13), and HARNESS SP IS ALWAYS NULL — no source we hold publishes it. `sp_note`
1341
+ * carries Racing Australia's favouritism marker on thoroughbred runners ('F' favourite, 'EF'
1342
+ * equal favourite) and is null everywhere else. Coverage RE-MEASURED 2026-09-08 over the whole
1343
+ * retained store, counting only rows whose `status` is `ran` (a barrier scratching is not a
1344
+ * starter and its price is not in this denominator): 88.6% of thoroughbred starters carry `sp`
1345
+ * (2,105 of 2,375); on the greyhound races collected since 2026-09-13, 2,054 of 2,077
1346
+ * Victorian starters do (98.9%) and 4233 of 4596 of the other states' starters do (92.1%),
1347
+ * both measured 2026-09-20. The Racing Australia block that held thoroughbred SP at 0% when
1348
+ * this was last measured on 2026-09-03 has lifted and the collector has been landing meetings
1349
+ * since. A null `sp` is unknown, never zero — RA prints no SP at all on some rows. On
1350
+ * placings, `sp` is copied from the matching `runners` entry when the response is built, so
1351
+ * the two can never disagree. On a race with no `runners` payload the `sp` and `sp_note` KEYS
1352
+ * ARE ABSENT from each placing — not present and null. The copy step is what creates them and
1353
+ * it is skipped when there is nothing to copy from, so a schema-validating consumer sees a
1354
+ * different object shape, not a null: read them with `.get()`. The keys are deliberately NOT
1355
+ * normalised in, because adding them here would change the shipped payload of every COMPLETE
1356
+ * race too, and nothing may move under a customer mid-integration. `field_status` tells you
1357
+ * which case you are in before you look.
1002
1358
  */
1003
1359
  placings?: Record<string, unknown>[];
1004
1360
  /**
@@ -1032,20 +1388,31 @@ export interface RaceResultOut {
1032
1388
  * completed the race — not just the placegetters — and is null whenever there is none: the
1033
1389
  * runner did not start, or it started and did not finish. `status` is ran | scratched |
1034
1390
  * late_scratching | reserve (an emergency that never got a start). `abnormal` is the official
1035
- * reason cell, and it carries TWO families that settle in OPPOSITE directions, so read it with
1036
- * `status` and never alone. (1) A DID-NOT-FINISH reason on a row whose `status` is `ran` —
1037
- * Fell, PulledUp, Disqualified, TailedOff, StayedInBox: the runner started and simply has no
1038
- * finishing position, and a bet on it lost. (2) A DID-NOT-START reason on a row whose `status`
1039
- * is `scratched` — 'SB', Racing Australia's code for scratched at the barrier: the runner was
1040
- * taken out of the market before the jump, so `position` is null, it is in no `placings`
1041
- * entry, and bets on it were refunded while the rest of the race was deducted. An 'SB' row may
1042
- * still carry `sp` — the last price Racing Australia printed for it, kept rather than blanked
1043
- * because it is a real market fact about the withdrawn runner. It is NOT the basis of the
1044
- * deduction, and you must never derive one from the other: `sp` comes from RA's results table
1045
- * while a deduction is struck by the settling bookmaker, and they do not agree. Measured
1046
- * 2026-09-08 across all five stored 'SB' rows that carry a price, 1/`sp` equals the published
1047
- * win deduction on NONE of them — Varejao, Sunshine Coast R4 2026-09-06, saddlecloth 5,
1048
- * withdrawn about 2 minutes 20 before the jump: `sp` 3.70, so 1/3.70 is 0.27 in the dollar,
1391
+ * reason cell, passed through VERBATIM from the source — on AU thoroughbred it is Racing
1392
+ * Australia's own Finish cell whenever that cell is not a number. THERE IS NO CLOSED LIST OF
1393
+ * CODES and you should not write a match statement that assumes one: the source invents them,
1394
+ * and a code nobody here has seen before will reach you unchanged rather than be dropped or
1395
+ * renamed. Everything stored to date, with counts as at 2026-09-14: Fell (101), TailedOff
1396
+ * (32), SB (25), PulledUp (8), FF (1, Rosehill R4 on 2026-09-12), LP (1). Treat `abnormal` as
1397
+ * the authoritative explanation for a missing `position`, and treat an unfamiliar value as a
1398
+ * did-not-finish unless `status` says otherwise — which is exactly what this API does: only
1399
+ * codes PROVEN to mean the runner never left the barrier flip `status` to `scratched`, and
1400
+ * that list currently holds one entry, 'SB'. Anything else stays `ran`, deliberately, because
1401
+ * misreading a starter as a non-starter turns a losing bet into a refund. It carries TWO
1402
+ * families that settle in OPPOSITE directions, so read it with `status` and never alone. (1) A
1403
+ * DID-NOT-FINISH reason on a row whose `status` is `ran` — Fell, PulledUp, Disqualified,
1404
+ * TailedOff, StayedInBox, FF and LP are the ones seen so far: the runner started and simply
1405
+ * has no finishing position, and a bet on it lost. (2) A DID-NOT-START reason on a row whose
1406
+ * `status` is `scratched` — 'SB', Racing Australia's code for scratched at the barrier: the
1407
+ * runner was taken out of the market before the jump, so `position` is null, it is in no
1408
+ * `placings` entry, and bets on it were refunded while the rest of the race was deducted. An
1409
+ * 'SB' row may still carry `sp` — the last price Racing Australia printed for it, kept rather
1410
+ * than blanked because it is a real market fact about the withdrawn runner. It is NOT the
1411
+ * basis of the deduction, and you must never derive one from the other: `sp` comes from RA's
1412
+ * results table while a deduction is struck by the settling bookmaker, and they do not agree.
1413
+ * Measured 2026-09-08 across all five stored 'SB' rows that carry a price, 1/`sp` equals the
1414
+ * published win deduction on NONE of them — Varejao, Sunshine Coast R4 2026-09-06, saddlecloth
1415
+ * 5, withdrawn about 2 minutes 20 before the jump: `sp` 3.70, so 1/3.70 is 0.27 in the dollar,
1049
1416
  * against a published `deductions` row of 0.18 win and 0.15 place; Dubai Dancer, Warrnambool
1050
1417
  * R5: 1/5.50 is 0.18 against a published 0.10. Settle from `deductions`, which is
1051
1418
  * authoritative. None of the five carries a `position` or a `margin`. Telling a barrier
@@ -1057,33 +1424,66 @@ export interface RaceResultOut {
1057
1424
  * barrier it jumped from — RA prints 0 there and we do not pass that sentinel on), and an
1058
1425
  * ordinary scratching carries `sp` null while an 'SB' may carry one. So `abnormal == 'SB'`
1059
1426
  * identifies a barrier scratching; `abnormal` null tells you only that it was not one, not how
1060
- * early it happened — use `deductions[].scratched_at` for that. On greyhound (Topaz)
1061
- * `abnormal` is null on every scratching and `status` is the discriminator: the LATE one is
1062
- * `late_scratching`, an earlier one is `scratched`. So today `late_scratching` is a
1063
- * greyhound-only value — a thoroughbred barrier scratching does NOT use it — and `scratched`
1064
- * does not carry identical meaning in the two codes. `deductions` carries `scratched_at` for
1065
- * either, and is the one cross-code way to see how close to the jump a runner came out.
1066
- * `dead_heat` is true on each runner sharing a position. `runner_ref` is a STABLE runner
1067
- * identifier from the official registry (namespaced: ra:<code> thoroughbred, grv:<id>
1068
- * greyhound) — it persists across meetings, so it is the join key for longitudinal work; null
1069
- * while a code's registry source is not yet wired. `trainer` and `jockey` come from the same
1070
- * official results source and may be null where that source is not yet live for the code. `sp`
1071
- * is the OFFICIAL STARTING PRICE as a decimal return on a $1 stake (4.6 means $4.60), null
1072
- * where it is unknown — never 0. It is the settling market's own price, not our consensus and
1073
- * not a bookmaker's fixed price: greyhound SP comes from the official Topaz feed, thoroughbred
1074
- * SP from the Racing Australia results table, and HARNESS SP IS ALWAYS NULL — no source we
1075
- * hold publishes it. `sp_note` carries Racing Australia's favouritism marker on thoroughbred
1076
- * runners ('F' favourite, 'EF' equal favourite) and is null everywhere else. Coverage
1077
- * RE-MEASURED 2026-09-08 over the whole retained store, counting only rows whose `status` is
1078
- * `ran` (a barrier scratching is not a starter and its price is not in this denominator):
1079
- * 99.8% of greyhound starters carry `sp` (14,540 of 14,568) and 88.6% of thoroughbred starters
1080
- * do (2,105 of 2,375). The Racing Australia block that held thoroughbred SP at 0% when this
1081
- * was last measured on 2026-09-03 has lifted and the collector has been landing meetings
1082
- * since. A null `sp` is unknown, never zero — RA prints no SP at all on some rows. Null
1083
- * `runners` means the full field is not available for this race — never an empty field;
1084
- * population is rolling out per racing code as our own results collection comes online.
1085
- * `placings` remains the source for the settling bookmaker's own fixed prices, and now also
1086
- * carries `sp` copied from here.
1427
+ * early it happened — use `deductions[].scratched_at` for that. On greyhound (our own
1428
+ * collection since 2026-09-13: FastTrack for Victorian tracks, the national results pages for
1429
+ * every other state) `abnormal` is null on every scratching and `status` carries three values:
1430
+ * `ran`, `scratched` (merged in from our own `deductions`, so it never says how late), and
1431
+ * `reserve` for a rug 9/10 reserve that never got a box. `late_scratching` was the retired
1432
+ * interim feed's greyhound-only value — a thoroughbred barrier scratching does NOT use it —
1433
+ * and it is no longer emitted; `scratched` does not carry identical meaning in the two codes.
1434
+ * `deductions` carries `scratched_at` for either, and is the one cross-code way to see how
1435
+ * close to the jump a runner came out. `dead_heat` is true on each runner sharing a position.
1436
+ * IDENTITY, CORRECTED 2026-09-21. `runner_ref` (`ra:<code>`) is Racing Australia's code for
1437
+ * this ENTRY: it joins the runner to RA's form page for that meeting and to our acceptance and
1438
+ * field rows for the same day, and it changes from meeting to meeting — measured 2026-09-21,
1439
+ * 1,450 of the 1,452 horses with two or more runs in our own settled results carried a
1440
+ * DIFFERENT code on every run. It is not a horse identity, and this description said it was
1441
+ * until today. `horse_ref` (`pe:<name>`) is: the registered name folded to letters and digits
1442
+ * (trailing country parenthetical off), the same value on results, the live board, the
1443
+ * closing-line and price-path archives, /v1/racing/horses/form, /v1/racing/horses/runs and the
1444
+ * backfill, and it is the key to join a horse across meetings. It is derived at serve time
1445
+ * from the name on the row, never stored, so every historical row carries it; null on
1446
+ * greyhound and harness runners. On GREYHOUNDS `runner_ref` IS the dog: `grv:<id>` for
1447
+ * Victorian tracks as FastTrack numbers the dog and `grsa:<id>` elsewhere as the national
1448
+ * results pages number it — per-dog registry ids that DO persist across meetings, in two
1449
+ * namespaces, so a dog racing in both is two refs until mapped. `runner_ref` is null while a
1450
+ * code's registry source is not yet wired. `trainer` and `jockey` come from the same official
1451
+ * results source and may be null where that source is not yet live for the code.
1452
+ * GREYHOUND-ONLY, ADDITIVE FROM 2026-09-21, five measured columns the results pages print and
1453
+ * this API used to drop. `first_split_s` is the first sectional in seconds exactly as the
1454
+ * source page printed it, null where the page printed none. `second_split_s` is the second
1455
+ * sectional, and is null on EVERY Victorian run because FastTrack publishes one split and not
1456
+ * two. `weight_kg` is the greyhound's race-day weight in kilograms, null where the page
1457
+ * carries no weight column at all — Western Australian meetings carry none (0 of 36 runner
1458
+ * rows sampled 2026-09-21). `pir` is FastTrack's position-in-running text passed through as a
1459
+ * STRING and never a number ("21" means 2nd at the first mark then 1st at the second),
1460
+ * Victorian runs only and null everywhere else. `grade` is the race's grade in the SOURCE'S
1461
+ * OWN WORDS — "M", "4/5" or "FFA" on the national pages, a race type such as "Tier 3 - Maiden"
1462
+ * on FastTrack — and the two vocabularies are NOT mapped onto each other, so do not compare a
1463
+ * Victorian grade with an interstate one. ⚠️ A STORED FIELD IS NEVER REWRITTEN, so a greyhound
1464
+ * field written before 2026-09-21 carries none of these five keys AT ALL — absent, not null —
1465
+ * and no thoroughbred field carries them in any era: read them with `.get()`. Measured on 46
1466
+ * freshly read race pages on 2026-09-21, a grade appeared on 46/46 races and 362/362 runner
1467
+ * rows, a first split on 288/362 and a weight on 295/362; the rows without them are
1468
+ * overwhelmingly rug 9/10 reserves, which never started and for which the page prints nothing.
1469
+ * `sp` is the OFFICIAL STARTING PRICE as a decimal return on a $1 stake (4.6 means $4.60),
1470
+ * null where it is unknown — never 0. It is the settling market's own price, not our consensus
1471
+ * and not a bookmaker's fixed price: thoroughbred SP comes from the Racing Australia results
1472
+ * table, greyhound SP from our own collection of published results (Victorian tracks via
1473
+ * FastTrack, every other state via the national results pages, both from 2026-09-13), and
1474
+ * HARNESS SP IS ALWAYS NULL — no source we hold publishes it. `sp_note` carries Racing
1475
+ * Australia's favouritism marker on thoroughbred runners ('F' favourite, 'EF' equal favourite)
1476
+ * and is null everywhere else. Coverage RE-MEASURED 2026-09-08 over the whole retained store,
1477
+ * counting only rows whose `status` is `ran` (a barrier scratching is not a starter and its
1478
+ * price is not in this denominator): 88.6% of thoroughbred starters carry `sp` (2,105 of
1479
+ * 2,375); on the greyhound races collected since 2026-09-13, 2,054 of 2,077 Victorian starters
1480
+ * do (98.9%) and 4233 of 4596 of the other states' starters do (92.1%), both measured
1481
+ * 2026-09-20. The Racing Australia block that held thoroughbred SP at 0% when this was last
1482
+ * measured on 2026-09-03 has lifted and the collector has been landing meetings since. A null
1483
+ * `sp` is unknown, never zero — RA prints no SP at all on some rows. Null `runners` means the
1484
+ * full field is not available for this race — never an empty field; AU thoroughbred and AU
1485
+ * greyhound races carry one. `placings` remains the source for the settling bookmaker's own
1486
+ * fixed prices, and now also carries `sp` copied from here.
1087
1487
  */
1088
1488
  runners?: Record<string, unknown>[] | null;
1089
1489
  /**
@@ -1095,35 +1495,38 @@ export interface RaceResultOut {
1095
1495
  * this race has not been yet; `field_note` names the UTC instant by which it will have run.
1096
1496
  * `overdue`: that pass has been and gone without writing a field, but the collector still
1097
1497
  * selects this row as a candidate and keeps re-attempting it — this state is NOT terminal. For
1098
- * AU greyhound the Topaz enricher re-reads every field-less row on each daily run for ten days
1099
- * after the race (its own LOOKBACK_DAYS); for AU thoroughbred a bounded re-fetch reaches six
1100
- * days but is run by hand rather than on a schedule. Most rows that reach `overdue` stay null,
1101
- * so do not build on a field arriving — but do not write the race off either, and `field_note`
1102
- * gives the horizon. `unavailable`: no scheduled pass still selects this row. Its collector's
1103
- * own candidate window closed at the instant in `field_note` and no field was written — the
1104
- * pass either never matched the race or refused an ambiguous match, which it does rather than
1105
- * write another race's field. `unsupported`: no collector has ever existed for this
1106
- * category/country — AU harness, NZ thoroughbred and NZ harness, 1,254 of the 5,577 stored
1107
- * results on 2026-09-10, plus any other country that appears because an AU book listed the
1108
- * meeting — and no amount of waiting will produce one. `not_applicable`: the race did not run
1109
- * (abandoned, postponed or transferred) and carries no field, so there is nothing to collect.
1110
- * TRANSITIONS, stated explicitly because acting on the wrong one costs you data. The scheduled
1111
- * progression is pending -> overdue -> unavailable, one direction only. It is bounded at every
1112
- * step by a collector's own window rather than by hope: a race can sit
1113
- * matched-and-never-resulted forever (The Gardens, 2026-09-05, 12 races), and an open-ended
1114
- * `pending` would have you polling it forever. Any of those three becomes `complete` the
1115
- * moment a field is written, AND THAT INCLUDES `unavailable` — nothing un-writes a `runners`
1116
- * array, so a collector re-run by hand, a widened window or a late upstream publication can
1117
- * still fill a row we have stopped scheduling passes for. So `unavailable` means 'nothing
1118
- * further is scheduled', never 'this can never arrive'; if you reconcile, re-read rather than
1119
- * caching it as final. `unsupported` and `not_applicable` are properties of the racing code
1120
- * and of the race itself, not of a schedule, and do not change on their own.
1498
+ * AU greyhound the hourly pass for its state (FastTrack for Victoria, the national results
1499
+ * pages elsewhere) re-reads every field-less race inside its seven-day window; for AU
1500
+ * thoroughbred a bounded re-fetch reaches six days but is run by hand rather than on a
1501
+ * schedule. Most rows that reach `overdue` stay null, so do not build on a field arriving —
1502
+ * but do not write the race off either, and `field_note` gives the horizon. `unavailable`: no
1503
+ * scheduled pass still selects this row. Its collector's own candidate window closed at the
1504
+ * instant in `field_note` and no field was written — the pass either never matched the race or
1505
+ * refused an ambiguous match, which it does rather than write another race's field.
1506
+ * `unsupported`: no collector has ever existed for this category/country — AU harness, NZ
1507
+ * thoroughbred and NZ harness, 1,254 of the 5,577 stored results on 2026-09-10, plus any other
1508
+ * country that appears because an AU book listed the meeting — and no amount of waiting will
1509
+ * produce one. `not_applicable`: the race did not run (abandoned, postponed or transferred)
1510
+ * and carries no field, so there is nothing to collect. TRANSITIONS, stated explicitly because
1511
+ * acting on the wrong one costs you data. The scheduled progression is pending -> overdue ->
1512
+ * unavailable, one direction only. It is bounded at every step by a collector's own window
1513
+ * rather than by hope: a race can sit matched-and-never-resulted forever (The Gardens,
1514
+ * 2026-09-05, 12 races), and an open-ended `pending` would have you polling it forever. Any of
1515
+ * those three becomes `complete` the moment a field is written, AND THAT INCLUDES
1516
+ * `unavailable` — nothing un-writes a `runners` array, so a collector re-run by hand, a
1517
+ * widened window or a late upstream publication can still fill a row we have stopped
1518
+ * scheduling passes for. So `unavailable` means 'nothing further is scheduled', never 'this
1519
+ * can never arrive'; if you reconcile, re-read rather than caching it as final. `unsupported`
1520
+ * and `not_applicable` are properties of the racing code and of the race itself, not of a
1521
+ * schedule, and do not change on their own.
1121
1522
  */
1122
1523
  field_status?: string;
1123
1524
  /**
1124
- * Which collector owns `runners` for this race: `racing_australia` (AU thoroughbred), `topaz`
1125
- * (AU greyhound), or null where no collector exists. Set on `pending`, `overdue`,
1126
- * `unavailable` and `complete` rows alike — it names who WOULD fill the field, not who did.
1525
+ * Which collector owns `runners` for this race: `racing_australia` (AU thoroughbred),
1526
+ * `fasttrack` (AU greyhound at Victorian tracks) and `grsa` (AU greyhound everywhere else),
1527
+ * the last two our own collection, or null where no collector exists. Set on `pending`,
1528
+ * `overdue`, `unavailable` and `complete` rows alike — it names who WOULD fill the field, not
1529
+ * who did.
1127
1530
  */
1128
1531
  field_source?: string | null;
1129
1532
  /**
@@ -1331,6 +1734,22 @@ export interface ResultsCoverageOut {
1331
1734
  field_scope: string;
1332
1735
  }
1333
1736
 
1737
+ export interface RewardIn {
1738
+ /** `cash` or `credits`. */
1739
+ reward_type: string;
1740
+ /**
1741
+ * Where a cash remittance should go, if it differs from the account email. Ignored for
1742
+ * `credits`.
1743
+ */
1744
+ payout_email?: string | null;
1745
+ /**
1746
+ * Requested referral code, first call only. 3-24 characters, lowercase letters, digits and
1747
+ * hyphens. Ignored once a code has been issued — a code that changes is a dead link on
1748
+ * somebody's blog.
1749
+ */
1750
+ code?: string | null;
1751
+ }
1752
+
1334
1753
  export interface RunPIR {
1335
1754
  /** Metres from the finish, e.g. 800. */
1336
1755
  at_m: number;
@@ -1340,20 +1759,67 @@ export interface RunPIR {
1340
1759
  export interface RunPlacegetter {
1341
1760
  position: number;
1342
1761
  name: string;
1343
- /** That horse's own stable ra: code — walkable straight back into this endpoint. */
1762
+ /**
1763
+ * That horse's own ra: code for that meeting — walkable straight back into this endpoint,
1764
+ * though it is the entry code and not a horse identity.
1765
+ */
1344
1766
  runner_ref?: string | null;
1345
1767
  weight_kg?: number | null;
1346
1768
  }
1347
1769
 
1770
+ export interface RunsSourceBlock {
1771
+ /**
1772
+ * Where the data comes from: our own collection of published AU thoroughbred results (Racing
1773
+ * Australia's results pages, read hourly by our results collector), flattened to one row per
1774
+ * horse per race and refreshed hourly. Not Racing Australia's form page — that is
1775
+ * /v1/racing/horses/form.
1776
+ */
1777
+ feed?: string;
1778
+ region?: string;
1779
+ sport?: string;
1780
+ /**
1781
+ * The first complete card in the store. A horse's record here starts here — it is not a
1782
+ * career.
1783
+ */
1784
+ coverage_from?: string;
1785
+ /** Sydney date of the latest race in the store. */
1786
+ coverage_to?: string | null;
1787
+ /**
1788
+ * Which of our collectors can contribute a run. AU thoroughbred results come from one: the
1789
+ * Racing Australia results pages.
1790
+ */
1791
+ collectors?: string[];
1792
+ /** The store is rebuilt from our own results at :59 each hour, after the results pass. */
1793
+ refreshed_hourly?: boolean;
1794
+ }
1795
+
1348
1796
  export interface SourceBlock {
1797
+ /**
1798
+ * Where the data comes from: our own collection of published AU greyhound results, Victorian
1799
+ * tracks via FastTrack (runner_ref grv:) and every other state via the national results pages
1800
+ * (runner_ref grsa:). Runs on or after `results_from` are races we settled ourselves; earlier
1801
+ * runs are the same pages read back by our history collector. Refreshed hourly.
1802
+ */
1349
1803
  feed?: string;
1350
1804
  region?: string;
1351
1805
  sport?: string;
1352
- coverage_from: string;
1353
- /** Meeting date of the latest run held, i.e. how far the last sync got. */
1806
+ /** Sydney date of the OLDEST run in the store. A dog's record here starts here. */
1807
+ coverage_from?: string;
1808
+ /**
1809
+ * Sydney date our own settled results begin. A run on or after it has `origin: results` and a
1810
+ * `result_id` that joins to /v1/racing/results; an earlier one has `origin: history` and does
1811
+ * not.
1812
+ */
1813
+ results_from?: string;
1814
+ /** Sydney date of the latest race in the store. */
1354
1815
  coverage_to?: string | null;
1355
- /** UTC timestamp of the last Topaz sync into this store. */
1356
- synced_at?: string | null;
1816
+ /**
1817
+ * Which of our collectors can contribute a run: `fasttrack` = Victorian tracks, `grsa` = every
1818
+ * other state.
1819
+ */
1820
+ collectors?: string[];
1821
+ /** The store is rebuilt from our results at :58 each hour, after both collectors have written. */
1822
+ refreshed_hourly?: boolean;
1357
1823
  }
1358
1824
 
1359
1825
  export interface SplitRecord {
@@ -1408,6 +1874,88 @@ export interface SportsArbOut {
1408
1874
  selections: Record<string, unknown>[];
1409
1875
  }
1410
1876
 
1877
+ export interface TeamKeyIn {
1878
+ /**
1879
+ * What this key is for — 'results worker', 'Jo's laptop'. Shown in the pool view and in your
1880
+ * usage breakdown.
1881
+ */
1882
+ label: string;
1883
+ /**
1884
+ * Optional. The most credits THIS key may spend per month, hard, inside the account's shared
1885
+ * pool — between 1 and the plan's own monthly allowance. Omit it (or send null) and the key
1886
+ * may spend the whole pool. Caps reset with the pool on the 1st.
1887
+ */
1888
+ monthly_cap?: number | null;
1889
+ }
1890
+
1891
+ export interface TeamKeyListOut {
1892
+ plan: string;
1893
+ allowance: number;
1894
+ keys_in_use: number;
1895
+ pool_credits_used: number;
1896
+ pool_credits_limit: number;
1897
+ pool_credits_remaining: number;
1898
+ max_monthly_cap?: number;
1899
+ primary: TeamKeyOut;
1900
+ team: TeamKeyOut[];
1901
+ }
1902
+
1903
+ export interface TeamKeyMintOut {
1904
+ key_id: string;
1905
+ /**
1906
+ * The team key. Shown ONCE; it is stored hashed and cannot be recovered — only revoked and
1907
+ * re-minted.
1908
+ */
1909
+ api_key: string;
1910
+ label: string;
1911
+ plan: string;
1912
+ /** Keys this account may hold, primary included. */
1913
+ allowance: number;
1914
+ keys_in_use: number;
1915
+ pool_credits_limit: number;
1916
+ pool_credits_used: number;
1917
+ monthly_cap?: number | null;
1918
+ /** The largest cap this plan accepts — its own monthly allowance. */
1919
+ max_monthly_cap?: number;
1920
+ }
1921
+
1922
+ export interface TeamKeyOut {
1923
+ key_id: string;
1924
+ key_hint: string;
1925
+ label: string | null;
1926
+ plan: string;
1927
+ created_at?: string | null;
1928
+ last_used_at?: string | null;
1929
+ period_calls?: number;
1930
+ period_credits?: number;
1931
+ /** This key's hard per-month credit cap, or null for no cap. */
1932
+ monthly_cap?: number | null;
1933
+ /**
1934
+ * Credits charged against this key's own counter this period. That is the number the cap is
1935
+ * checked against; it stays 0 on an uncapped team key, whose spending is only ever charged to
1936
+ * the pool row. Use period_credits for what an uncapped key has spent.
1937
+ */
1938
+ key_credits_used?: number;
1939
+ }
1940
+
1941
+ /**
1942
+ * Both fields are optional; only the ones PRESENT in the body are written. `monthly_cap: null`
1943
+ * is meaningful (remove the cap) and must not read as "unchanged", so the handler asks
1944
+ * model_fields_set which keys the caller actually sent rather than inferring intent from a
1945
+ * None that pydantic would produce either way.
1946
+ */
1947
+ export interface TeamKeyPatchIn {
1948
+ /** New label for this key. Omit to leave it alone. */
1949
+ label?: string | null;
1950
+ /**
1951
+ * Optional. The most credits THIS key may spend per month, hard, inside the account's shared
1952
+ * pool — between 1 and the plan's own monthly allowance. Omit it (or send null) and the key
1953
+ * may spend the whole pool. Caps reset with the pool on the 1st. Send null to REMOVE an
1954
+ * existing cap; omit the field entirely to leave the cap as it is.
1955
+ */
1956
+ monthly_cap?: number | null;
1957
+ }
1958
+
1411
1959
  export interface TrackConditionsOut {
1412
1960
  /** Meeting day, Australia/Sydney calendar date. */
1413
1961
  date: string;
@@ -1415,10 +1963,21 @@ export interface TrackConditionsOut {
1415
1963
  note: string;
1416
1964
  }
1417
1965
 
1418
- export interface TrainerRef {
1419
- /** Topaz trainerId. Stable across meetings. */
1420
- id?: number | null;
1421
- name?: string | null;
1966
+ export interface UpstreamBlock {
1967
+ /**
1968
+ * `ok` when Racing Australia is answering form reads, `walled` when it is serving its
1969
+ * bot-protection challenge to every fresh read and nothing can be collected.
1970
+ */
1971
+ state: string;
1972
+ /**
1973
+ * UTC time of the first refusal in the current streak, null when `state` is `ok`. This is the
1974
+ * field that tells you a stalled queue is hours old rather than minutes.
1975
+ */
1976
+ walled_since?: string | null;
1977
+ /** How long the current streak has run, in hours, null when `state` is `ok`. */
1978
+ walled_hours?: number | null;
1979
+ /** One sentence saying what that means for this job, in plain words. */
1980
+ note: string;
1422
1981
  }
1423
1982
 
1424
1983
  export interface UsageOut {
@@ -1441,6 +2000,9 @@ export interface UsageOut {
1441
2000
  recent_activity?: Record<string, unknown>[];
1442
2001
  usage_by_endpoint_period?: Record<string, unknown>[];
1443
2002
  recent_activity_period?: Record<string, unknown>[];
2003
+ pool?: Record<string, unknown> | null;
2004
+ overage?: Record<string, unknown>;
2005
+ credits_remaining_with_overage?: number;
1444
2006
  plans?: Record<string, Record<string, unknown>>;
1445
2007
  }
1446
2008