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.
@@ -118,11 +118,18 @@ export interface BackfillStatusOut {
118
118
  estimated_ready?: string | null;
119
119
  /** Completed rows you have not yet read. */
120
120
  ready_to_collect: number;
121
+ /**
122
+ * Whether Racing Australia is answering form reads at all. Added 2026-09-21 and purely
123
+ * additive: nothing else on this response, and nothing about billing or the queue, changed
124
+ * with it.
125
+ */
126
+ upstream: UpstreamBlock;
121
127
  }
122
128
  export interface BackfillSubmitIn {
123
129
  /**
124
- * Horse names or ra: refs. Max 500 per call. A ref is exact; a name is resolved against
125
- * acceptances and archived results.
130
+ * Horse names, `pe:` horse_refs or `ra:` entry codes, in any mix. Max 500 per call. A name and
131
+ * its horse_ref resolve identically, against acceptances and archived results; an `ra:` code
132
+ * pins one meeting's entry.
126
133
  */
127
134
  horses: string[];
128
135
  /** Your own tag for this batch, echoed back on status. */
@@ -134,8 +141,9 @@ export interface BackfillSubmitOut {
134
141
  /** Horses accepted and waiting on the worker. */
135
142
  queued: number;
136
143
  /**
137
- * Horses we already hold a fresh copy of. Collectable immediately, still billed at 3 credits
138
- * each.
144
+ * Horses we already hold a fresh copy of — matched on the HORSE since 2026-09-21, so a page
145
+ * collected under any of its Racing Australia entry codes counts. Collectable immediately,
146
+ * still billed at 3 credits each.
139
147
  */
140
148
  already_stored: number;
141
149
  /** Already queued for you; not queued twice. */
@@ -169,6 +177,40 @@ export interface BestPricesOut {
169
177
  commence_time: string;
170
178
  selections: Record<string, unknown>[];
171
179
  }
180
+ export interface BulkCatalogueOut {
181
+ datasets: BulkDatasetOut[];
182
+ formats?: string[];
183
+ day_plans: string;
184
+ month_plans: string;
185
+ credits: Record<string, number>;
186
+ schedule: string;
187
+ notes?: string[];
188
+ }
189
+ export interface BulkDatasetOut {
190
+ name: string;
191
+ title: string;
192
+ description: string;
193
+ grain: string;
194
+ /** Column order of every file in this dataset, read from the newest file's sidecar. */
195
+ columns?: string[];
196
+ available_from: string;
197
+ /** Daily files, oldest first. Business and above. */
198
+ days?: BulkFileOut[];
199
+ /** Monthly bundles, oldest first. Platform and above. */
200
+ months?: BulkFileOut[];
201
+ }
202
+ export interface BulkFileOut {
203
+ period: string;
204
+ /** day (YYYY-MM-DD, Sydney meeting date) or month (YYYY-MM). */
205
+ kind: string;
206
+ rows?: number | null;
207
+ generated_at?: string | null;
208
+ /** Size per format: parquet, csv (gzip). */
209
+ bytes?: Record<string, number>;
210
+ /** Digest per format; also the ETag. */
211
+ sha256?: Record<string, string>;
212
+ url: string;
213
+ }
172
214
  export interface ChangedRaceOut {
173
215
  /**
174
216
  * Stable race identifier — the same id /v1/racing/next-to-go, /v1/racing/price-history and
@@ -298,6 +340,124 @@ export interface ClosingLinesOut {
298
340
  resulted_rows: number;
299
341
  rows: Record<string, unknown>[];
300
342
  }
343
+ export interface ClvBetIn {
344
+ /** Your own id for this bet, echoed back unchanged. */
345
+ ref?: string | null;
346
+ /**
347
+ * The race_id from /v1/racing/next-to-go, /events, /results or the archive. The most reliable
348
+ * way to name a race; use venue + meeting_date + race_number only when you do not hold it.
349
+ */
350
+ race_id?: string | null;
351
+ /** Venue name as our feeds spell it (case-insensitive). Needs meeting_date and race_number. */
352
+ venue?: string | null;
353
+ race_number?: number | null;
354
+ /** AET meeting date, YYYY-MM-DD. */
355
+ meeting_date?: string | null;
356
+ /**
357
+ * Registry id as served on /results and the archive — ra:<horsecode> or grv:<dogId>. Exact
358
+ * match; the most reliable way to name a runner.
359
+ */
360
+ runner_ref?: string | null;
361
+ /** Saddlecloth number. */
362
+ runner_number?: number | null;
363
+ /**
364
+ * Runner name; matched the same way the feeds match names (case, punctuation and country
365
+ * suffixes ignored).
366
+ */
367
+ runner_name?: string | null;
368
+ /**
369
+ * bookmaker_key the bet was struck with. When given, `clv_pct` is measured against THAT book's
370
+ * closing price and `close_book` is filled; when absent, against the market reference chosen
371
+ * by `reference`.
372
+ */
373
+ bookmaker?: string | null;
374
+ /** Decimal odds you took. */
375
+ price: number;
376
+ /** Stake for the P&L line. Defaults to one flat unit. */
377
+ stake?: number;
378
+ }
379
+ export interface ClvBetOut {
380
+ ref?: string | null;
381
+ /**
382
+ * True when the race AND the runner were found in the archive. False rows carry `reason` and
383
+ * no numbers.
384
+ */
385
+ resolved: boolean;
386
+ /**
387
+ * race_not_found · race_ambiguous · runner_not_found · runner_ambiguous · bookmaker_withheld ·
388
+ * no_closing_line · no_closing_line_for_book.
389
+ */
390
+ reason?: string | null;
391
+ race_id?: string | null;
392
+ venue?: string | null;
393
+ race_number?: number | null;
394
+ runner_key?: string | null;
395
+ runner_name?: string | null;
396
+ runner_number?: number | null;
397
+ runner_ref?: string | null;
398
+ bookmaker?: string | null;
399
+ price_taken: number;
400
+ stake: number;
401
+ /** The named bookmaker's closing win price, when it recorded a closing line. */
402
+ close_book?: number | null;
403
+ /** Highest closing win price across books. */
404
+ close_best?: number | null;
405
+ /** Median closing win price across books. */
406
+ close_median?: number | null;
407
+ /** Books with a closing line for this runner. */
408
+ books_at_close?: number;
409
+ /**
410
+ * The close `clv_pct` is measured against: close_book when a bookmaker was named and priced
411
+ * the close, else the market reference.
412
+ */
413
+ close_reference?: number | null;
414
+ /** (price_taken / close_reference − 1) × 100. Positive means you beat the close. */
415
+ clv_pct?: number | null;
416
+ beat_close?: boolean | null;
417
+ finish_position?: number | null;
418
+ /** final · interim · null (not yet resulted). */
419
+ result_status?: string | null;
420
+ /** finish_position == 1 on a final result; null until final. */
421
+ won?: boolean | null;
422
+ /** stake × (price − 1) on a win, −stake on a loss, null until the result is final. */
423
+ pnl?: number | null;
424
+ }
425
+ export interface ClvIn {
426
+ /** Up to 200 bets per request. */
427
+ bets: ClvBetIn[];
428
+ /**
429
+ * The market close used when a bet names no bookmaker, and reported as close_best /
430
+ * close_median on every bet regardless: `best` is the highest closing win price across books,
431
+ * `median` the median.
432
+ */
433
+ reference?: string;
434
+ }
435
+ export interface ClvOut {
436
+ reference: string;
437
+ /** Earliest race day the archive holds; bets before it cannot resolve. */
438
+ archive_floor?: string | null;
439
+ summary: ClvSummaryOut;
440
+ bets: ClvBetOut[];
441
+ notes?: string[];
442
+ }
443
+ export interface ClvSummaryOut {
444
+ bets: number;
445
+ resolved: number;
446
+ unresolved: number;
447
+ /** Bets with a closing line to measure against. */
448
+ scored: number;
449
+ avg_clv_pct?: number | null;
450
+ median_clv_pct?: number | null;
451
+ beat_close?: number;
452
+ beat_close_rate_pct?: number | null;
453
+ /** Scored-or-resolved bets with a final result. */
454
+ resulted?: number;
455
+ wins?: number;
456
+ win_rate_pct?: number | null;
457
+ staked?: number | null;
458
+ pnl?: number | null;
459
+ roi_pct?: number | null;
460
+ }
301
461
  export interface ConnectorHealthOut {
302
462
  connector: string;
303
463
  last_ok: string | null;
@@ -306,101 +466,143 @@ export interface ConnectorHealthOut {
306
466
  records_written: number;
307
467
  message?: string | null;
308
468
  }
309
- export interface DogCandidate {
310
- dog_id: number;
311
- dog_name?: string | null;
312
- /** Career starts on this feed, scratchings included. */
313
- runs: number;
314
- /** Meeting date of the most recent run. */
315
- last_start?: string | null;
316
- }
317
469
  export interface GreyhoundFormOut {
318
- dog_id?: number | null;
319
- dog_name?: string | null;
320
- /** True when the name matched several dogs. `runs` is then empty and `candidates` lists them. */
321
- ambiguous: boolean;
322
- candidates?: DogCandidate[];
323
- runs?: GreyhoundRun[];
470
+ /** The dog's name as our results spell it. */
471
+ dog: string;
472
+ /** The identity the runs were grouped on (letters and digits). */
473
+ name_key: string;
474
+ /** Every registry id seen for this name, across both collectors. */
475
+ runner_refs: string[];
476
+ /** Runs held for this dog since `source.coverage_from`; `runs` is the newest `limit` of them. */
477
+ runs_total: number;
478
+ runs: GreyhoundRun[];
324
479
  source: SourceBlock;
325
480
  }
326
481
  export interface GreyhoundRun {
327
- /** Meeting date, AEST calendar day, YYYY-MM-DD. */
482
+ /** Sydney calendar date of the race. */
328
483
  date?: string | null;
329
- /** Official Topaz track name, sponsor included (e.g. 'Bet Deluxe Capalaba'). */
330
- track?: string | null;
331
- distance_m?: number | null;
332
- /** Topaz raceTypeCode, e.g. '5', 'M', 'X45'. */
333
- grade?: string | null;
484
+ /** Scheduled start, UTC. */
485
+ start_time?: string | null;
486
+ venue?: string | null;
334
487
  race_number?: number | null;
488
+ distance_m?: number | null;
489
+ race_name?: string | null;
490
+ track_condition?: string | null;
335
491
  box?: number | null;
336
- rug?: number | null;
337
- weight_kg?: number | null;
338
- /** Official starting price, decimal. */
339
- sp?: number | null;
340
- /** Finishing place. NULL when the dog did not complete the course or was scratched. */
492
+ /** Rug number; equals box for a starter. */
493
+ number?: number | null;
494
+ /**
495
+ * Finishing place. Null when the dog did not run (see `status`) or did not complete the
496
+ * course.
497
+ */
341
498
  position?: number | null;
342
- scratched: boolean;
343
- /** Fell, TailedOff, PulledUp, Disqualified or StayedInBox. NULL for a normal run. */
344
- abnormal?: string | null;
345
- time_s?: number | null;
346
- /** Winning time of this race, i.e. the place-1 dog's time. */
347
- win_time_s?: number | null;
348
- /** Margin to the winner in lengths, as Topaz publishes it (e.g. '5.50L'). */
499
+ /** `ran`, `scratched`, or `reserve` (a rug 9/10 reserve that never got a box). */
500
+ status?: string | null;
501
+ /**
502
+ * Lengths behind the winner as the source printed it; the winner's own row carries the winning
503
+ * margin on Victorian tracks and null elsewhere.
504
+ */
349
505
  margin?: string | null;
506
+ /** The dog's own finish time in seconds. */
507
+ time_s?: number | null;
508
+ /**
509
+ * First sectional in seconds, as the source page printed it. Null where it printed none — a
510
+ * reserve that never started has none, and some tracks publish no sectionals at all.
511
+ */
512
+ first_split_s?: number | null;
350
513
  /**
351
- * Margin to the winner in seconds. On the winner's own run this is the winning margin over the
352
- * runner-up.
514
+ * Second sectional in seconds. Null on every Victorian run: FastTrack publishes one split, not
515
+ * two.
353
516
  */
354
- margin_s?: number | null;
355
- first_split_time?: number | null;
356
- first_split_position?: number | null;
517
+ second_split_s?: number | null;
357
518
  /**
358
- * Second sectional split. Only some tracks time two points: 21% of all runs carry one (603,734
359
- * of 2,861,690, measured 2026-09-02).
519
+ * Race-day weight in kilograms. Null where the page prints no weight — Western Australian
520
+ * meetings carry no weight column at all.
360
521
  */
361
- second_split_time?: number | null;
362
- second_split_position?: number | null;
363
- /** The dog's grade entering this race, as Topaz graded it. */
364
- grade_in?: string | null;
522
+ weight_kg?: number | null;
365
523
  /**
366
- * The dog's grade after this race's result was applied. A value differing from grade_in is a
367
- * grade change — a win typically moves the dog down a number (5 -> 4).
524
+ * Position in running, FastTrack's own digits ("21" = 2nd then 1st at the marks), passed
525
+ * through as TEXT and not a number. Victorian runs only; null everywhere else.
368
526
  */
369
- grade_out?: string | null;
370
- /** Position in running, one digit per section. */
371
527
  pir?: string | null;
372
- trainer: TrainerRef;
373
- /** Topaz raceId. Every runner in the same race shares it. */
374
- topaz_race_id?: number | null;
375
- dog_id: number;
376
- dog_name?: string | null;
528
+ /**
529
+ * The race's grade in the SOURCE'S OWN WORDS — "M", "4/5", "FFA" on the national pages, a race
530
+ * type like "Tier 3 - Maiden" on FastTrack. The two vocabularies are NOT mapped onto each
531
+ * other and must not be compared across states. Null on runs whose field was stored before
532
+ * 2026-09-21.
533
+ */
534
+ grade?: string | null;
535
+ /** The winner's time in the same race, so `time_s - win_time_s` is the deficit. */
536
+ win_time_s?: number | null;
537
+ /** Official starting price, decimal return on $1. Null = unknown, never zero. */
538
+ sp?: number | null;
539
+ dead_heat?: boolean;
540
+ trainer?: string | null;
541
+ /** Dogs that ran in that race. */
542
+ field_size?: number | null;
543
+ /** Registry id of the dog as the collector for that track numbers it: grv:<id> or grsa:<id>. */
544
+ runner_ref?: string | null;
545
+ /** `fasttrack` or `grsa` — which collector wrote the race. */
546
+ source?: string | null;
547
+ /**
548
+ * `results` — the race is one we settled and serve on /v1/racing/results; `history` — the race
549
+ * was read back off the same official results page by our history collector and has no results
550
+ * row.
551
+ */
552
+ origin?: string | null;
553
+ /**
554
+ * Id of the /v1/racing/results row this run came from. On an `origin: history` run it is a
555
+ * synthetic `hist:…` id that joins to nothing.
556
+ */
557
+ result_id?: string | null;
377
558
  }
378
- export interface GreyhoundStatGroup {
559
+ export interface GreyhoundStatRow {
560
+ /** Set when `by` includes track. */
379
561
  track?: string | null;
562
+ /** Set when `by` includes distance. */
380
563
  distance_m?: number | null;
564
+ /** Set when `by` includes box. */
381
565
  box?: number | null;
382
- grade?: string | null;
566
+ /**
567
+ * Runs with status `ran` — scratchings and unused reserves are excluded from every figure
568
+ * here.
569
+ */
383
570
  starts: number;
384
571
  wins: number;
385
- /** Finishes in the first three. */
572
+ /** Finishes in the first three, wins included. */
386
573
  places: number;
387
574
  win_pct: number;
388
575
  place_pct: number;
576
+ /** Over the runs with a recorded time. */
389
577
  avg_time_s?: number | null;
390
578
  best_time_s?: number | null;
391
- avg_first_split?: number | null;
579
+ /**
580
+ * Mean first sectional over the runs in this group that carry one. Null when none does — the
581
+ * count behind it is not `starts`, because some tracks publish no sectionals.
582
+ */
583
+ avg_first_split_s?: number | null;
584
+ /** Mean official starting price over the runs that carry one. */
585
+ avg_sp?: number | null;
586
+ /** Sydney date of the most recent run in the group. */
587
+ last_start?: string | null;
392
588
  }
393
589
  export interface GreyhoundStatsOut {
394
- dog_id?: number | null;
395
- dog_name?: string | null;
396
- ambiguous: boolean;
397
- candidates?: DogCandidate[];
398
- group_by?: string[];
399
- groups?: GreyhoundStatGroup[];
590
+ dog: string;
591
+ name_key: string;
592
+ runner_refs: string[];
593
+ /** The dimensions the rows are grouped by, in the order given. */
594
+ by: string[];
595
+ /** One row per distinct combination, most starts first. */
596
+ rows: GreyhoundStatRow[];
597
+ /** The same figures over every run since `source.coverage_from`. */
598
+ overall: GreyhoundStatRow;
400
599
  source: SourceBlock;
401
600
  }
402
601
  export interface HorseCandidate {
403
- /** Stable ra: horsecode — re-query with this. */
602
+ /**
603
+ * Racing Australia entry code — re-query with this to pin one meeting's entry. `candidates` is
604
+ * empty from 2026-09-21: a name resolves to one horse. See `ambiguous`.
605
+ */
404
606
  runner_ref: string;
405
607
  runner_name?: string | null;
406
608
  /** Most recent date this ref appeared on our feeds (acceptance or result). */
@@ -429,11 +631,25 @@ export interface HorseCareer {
429
631
  export interface HorseFormOut {
430
632
  horse_name?: string | null;
431
633
  /**
432
- * Stable ra: horsecode — the same identifier on /v1/racing/results runners and
433
- * acceptance-enriched live runners.
634
+ * The HORSE: `pe:<name>`, the registered name folded to letters and digits. The same value on
635
+ * /v1/racing/results runners and placings, the live board, the closing-line and price-path
636
+ * archives, /v1/racing/horses/runs and the form backfill — and accepted as `horse` here. This
637
+ * is the key to join a horse across meetings.
638
+ */
639
+ horse_ref?: string | null;
640
+ /**
641
+ * Racing Australia's code for the ENTRY whose page was read — the same identifier on
642
+ * /v1/racing/results runners and acceptance-enriched live runners FOR THAT MEETING. It changes
643
+ * from meeting to meeting (measured 2026-09-21: 1,450 of 1,452 horses with two or more runs
644
+ * carried a different code on each), so it is not a horse identity — `horse_ref` is. Where a
645
+ * horse has several codes this is the most recently seen one.
434
646
  */
435
647
  runner_ref?: string | null;
436
- /** True when the name matched several horses. `runs` is then empty and `candidates` lists them. */
648
+ /**
649
+ * ALWAYS FALSE since 2026-09-21, and kept only so that callers reading it do not break. A name
650
+ * now resolves to one horse: several ra: codes under one name are the ordinary case (they are
651
+ * entry codes), not two horses. `candidates` is empty with it.
652
+ */
437
653
  ambiguous: boolean;
438
654
  candidates?: HorseCandidate[];
439
655
  profile?: HorseProfile | null;
@@ -509,6 +725,108 @@ export interface HorseRun {
509
725
  */
510
726
  placegetters?: RunPlacegetter[];
511
727
  }
728
+ export interface HorseRunRow {
729
+ /** Sydney calendar date of the race. */
730
+ date?: string | null;
731
+ /** Scheduled start, UTC. */
732
+ start_time?: string | null;
733
+ venue?: string | null;
734
+ race_number?: number | null;
735
+ distance_m?: number | null;
736
+ race_name?: string | null;
737
+ /** As published for the meeting, e.g. 'Good 4'. */
738
+ track_condition?: string | null;
739
+ /** Saddlecloth number. */
740
+ number?: number | null;
741
+ /** Barrier. Null on a scratching — a runner that never jumped has no barrier. */
742
+ box?: number | null;
743
+ /**
744
+ * Finishing place. Null when the horse did not start (see `status`) or started and did not
745
+ * finish (see `abnormal`).
746
+ */
747
+ position?: number | null;
748
+ /** `ran` or `scratched`, exactly as /v1/racing/results publishes it. */
749
+ status?: string | null;
750
+ /**
751
+ * Racing Australia's own Finish cell whenever it is not a number, passed through verbatim
752
+ * (Fell, PulledUp, TailedOff, LR, FF, LP, and 'SB' for scratched at the barrier). THERE IS NO
753
+ * CLOSED LIST — an unseen code reaches you unchanged. Read it with `status`.
754
+ */
755
+ abnormal?: string | null;
756
+ /** Lengths behind the winner as RA printed it ('0.75L'); null where it prints none. */
757
+ margin?: string | null;
758
+ /**
759
+ * The horse's own race time in seconds. NULL on every row today: RA's results table publishes
760
+ * no per-runner time (0 of 10,092 stored runners carry one, measured 2026-09-21).
761
+ * /v1/racing/horses/form carries RA's official race time where we hold that horse's page.
762
+ */
763
+ time_s?: number | null;
764
+ /** Official starting price, decimal return on $1. Null = unknown, never zero. */
765
+ sp?: number | null;
766
+ /** RA's favouritism marker: 'F' favourite, 'EF' equal favourite; null otherwise. */
767
+ sp_note?: string | null;
768
+ dead_heat?: boolean;
769
+ /** Trainer of the day for THIS run, as the result published it. */
770
+ trainer?: string | null;
771
+ /** Rider of the day for THIS run. */
772
+ jockey?: string | null;
773
+ /** Horses that ran in that race. */
774
+ field_size?: number | null;
775
+ /**
776
+ * The Racing Australia horsecode our results collector published for THIS entry — the same
777
+ * value on the matching /v1/racing/results runner. It is not a horse identity here: measured
778
+ * 2026-09-21 over the whole store, 10,092 runner rows carry 9,872 distinct codes across 7,326
779
+ * names, so the same horse usually carries a different code at each meeting. Group on
780
+ * `horse_ref`, not on this.
781
+ */
782
+ runner_ref?: string | null;
783
+ /**
784
+ * The horse this run belongs to: `pe:<name>`, the registered name folded to letters and
785
+ * digits. Identical on every run of the same horse, and the same value /v1/racing/results
786
+ * runners and placings, the live board, the closing-line and price-path archives and
787
+ * /v1/racing/horses/form carry — the key that joins a horse across meetings, where
788
+ * `runner_ref` joins one entry to one meeting.
789
+ */
790
+ horse_ref?: string | null;
791
+ /** Id of the /v1/racing/results row this run came from. */
792
+ result_id?: string | null;
793
+ }
794
+ export interface HorseRunsOut {
795
+ /** The horse's name as our results spell it. */
796
+ horse: string;
797
+ /** The identity the runs were grouped on (letters and digits). */
798
+ name_key: string;
799
+ /**
800
+ * `name_key` as the identifier every thoroughbred surface publishes: `pe:<name_key>`. Pass it
801
+ * back here, or to /v1/racing/horses/form, and read it off /v1/racing/results runners and
802
+ * placings, the live board and the closing-line and price-path archives to gather one horse
803
+ * across meetings.
804
+ */
805
+ horse_ref?: string | null;
806
+ /**
807
+ * Every Racing Australia horsecode our results published under this name, sorted. Several is
808
+ * normal, not an error — see `runs[].runner_ref`.
809
+ */
810
+ runner_refs: string[];
811
+ /** Runs held for this horse since `source.coverage_from`; `runs` is the newest `limit` of them. */
812
+ runs_total: number;
813
+ runs: HorseRunRow[];
814
+ /** Over EVERY held run, not just the ones returned. */
815
+ stats: HorseRunsStats;
816
+ source: RunsSourceBlock;
817
+ }
818
+ export interface HorseRunsStats {
819
+ /**
820
+ * Runs with status `ran` — scratchings are excluded from every figure here, which is why
821
+ * `starts` can be lower than `runs_total`.
822
+ */
823
+ starts: number;
824
+ wins: number;
825
+ /** Finishes in the first three, wins included. */
826
+ places: number;
827
+ win_pct: number;
828
+ place_pct: number;
829
+ }
512
830
  export interface HorseSource {
513
831
  feed?: string;
514
832
  region?: string;
@@ -685,6 +1003,25 @@ export interface NextRaceOut {
685
1003
  cached?: boolean | null;
686
1004
  cache_age_seconds?: number | null;
687
1005
  }
1006
+ export interface OverageIn {
1007
+ /**
1008
+ * true lets this account run past its monthly allowance, up to the ceiling, and be invoiced
1009
+ * for the excess. false restores the hard 402 at the allowance.
1010
+ */
1011
+ enabled: boolean;
1012
+ }
1013
+ export interface OverageOut {
1014
+ enabled: boolean;
1015
+ eligible: boolean;
1016
+ rate_per_1000_cents: number;
1017
+ max_credits: number;
1018
+ ceiling: number;
1019
+ credits_over_this_period: number;
1020
+ estimated_cents_this_period: number;
1021
+ plan: string;
1022
+ credits_limit: number;
1023
+ credits_used: number;
1024
+ }
688
1025
  export interface PremiershipOut {
689
1026
  role: string;
690
1027
  season_start_year?: number | null;
@@ -932,28 +1269,35 @@ export interface RaceResultOut {
932
1269
  */
933
1270
  status_note?: string | null;
934
1271
  /**
935
- * The placegetters: {position, number, name, win_price, place_price, sp, sp_note}. `win_price`
936
- * and `place_price` are the SETTLING BOOKMAKER'S FIXED prices. `sp` is the OFFICIAL STARTING
937
- * PRICE as a decimal return on a $1 stake (4.6 means $4.60), null where it is unknown — never
938
- * 0. It is the settling market's own price, not our consensus and not a bookmaker's fixed
939
- * price: greyhound SP comes from the official Topaz feed, thoroughbred SP from the Racing
940
- * Australia results table, and HARNESS SP IS ALWAYS NULL — no source we hold publishes it.
941
- * `sp_note` carries Racing Australia's favouritism marker on thoroughbred runners ('F'
942
- * favourite, 'EF' equal favourite) and is null everywhere else. Coverage RE-MEASURED
943
- * 2026-09-08 over the whole retained store, counting only rows whose `status` is `ran` (a
944
- * barrier scratching is not a starter and its price is not in this denominator): 99.8% of
945
- * greyhound starters carry `sp` (14,540 of 14,568) and 88.6% of thoroughbred starters do
946
- * (2,105 of 2,375). The Racing Australia block that held thoroughbred SP at 0% when this was
947
- * last measured on 2026-09-03 has lifted and the collector has been landing meetings since. A
948
- * null `sp` is unknown, never zero — RA prints no SP at all on some rows. On placings, `sp` is
949
- * copied from the matching `runners` entry when the response is built, so the two can never
950
- * disagree. On a race with no `runners` payload the `sp` and `sp_note` KEYS ARE ABSENT from
951
- * each placing — not present and null. The copy step is what creates them and it is skipped
952
- * when there is nothing to copy from, so a schema-validating consumer sees a different object
953
- * shape, not a null: read them with `.get()`. The keys are deliberately NOT normalised in,
954
- * because adding them here would change the shipped payload of every COMPLETE race too, and
955
- * nothing may move under a customer mid-integration. `field_status` tells you which case you
956
- * are in before you look.
1272
+ * The placegetters: {position, number, name, win_price, place_price, sp, sp_note, horse_ref}.
1273
+ * `win_price` and `place_price` are the SETTLING BOOKMAKER'S FIXED prices. `horse_ref` (added
1274
+ * 2026-09-21) is the same `pe:<name>` horse identity the `runners` entries carry, derived at
1275
+ * serve time from the name on the placing and present on every placing — null off
1276
+ * thoroughbred. It is the key that joins this placing to the same horse on another meeting;
1277
+ * `runner_ref` on the matching `runners` entry is Racing Australia's per-MEETING entry code
1278
+ * and will not. `sp` is the OFFICIAL STARTING PRICE as a decimal return on a $1 stake (4.6
1279
+ * means $4.60), null where it is unknown — never 0. It is the settling market's own price, not
1280
+ * our consensus and not a bookmaker's fixed price: thoroughbred SP comes from the Racing
1281
+ * Australia results table, greyhound SP from our own collection of published results
1282
+ * (Victorian tracks via FastTrack, every other state via the national results pages, both from
1283
+ * 2026-09-13), and HARNESS SP IS ALWAYS NULL — no source we hold publishes it. `sp_note`
1284
+ * carries Racing Australia's favouritism marker on thoroughbred runners ('F' favourite, 'EF'
1285
+ * equal favourite) and is null everywhere else. Coverage RE-MEASURED 2026-09-08 over the whole
1286
+ * retained store, counting only rows whose `status` is `ran` (a barrier scratching is not a
1287
+ * starter and its price is not in this denominator): 88.6% of thoroughbred starters carry `sp`
1288
+ * (2,105 of 2,375); on the greyhound races collected since 2026-09-13, 2,054 of 2,077
1289
+ * Victorian starters do (98.9%) and 4233 of 4596 of the other states' starters do (92.1%),
1290
+ * both measured 2026-09-20. The Racing Australia block that held thoroughbred SP at 0% when
1291
+ * this was last measured on 2026-09-03 has lifted and the collector has been landing meetings
1292
+ * since. A null `sp` is unknown, never zero — RA prints no SP at all on some rows. On
1293
+ * placings, `sp` is copied from the matching `runners` entry when the response is built, so
1294
+ * the two can never disagree. On a race with no `runners` payload the `sp` and `sp_note` KEYS
1295
+ * ARE ABSENT from each placing — not present and null. The copy step is what creates them and
1296
+ * it is skipped when there is nothing to copy from, so a schema-validating consumer sees a
1297
+ * different object shape, not a null: read them with `.get()`. The keys are deliberately NOT
1298
+ * normalised in, because adding them here would change the shipped payload of every COMPLETE
1299
+ * race too, and nothing may move under a customer mid-integration. `field_status` tells you
1300
+ * which case you are in before you look.
957
1301
  */
958
1302
  placings?: Record<string, unknown>[];
959
1303
  /**
@@ -987,20 +1331,31 @@ export interface RaceResultOut {
987
1331
  * completed the race — not just the placegetters — and is null whenever there is none: the
988
1332
  * runner did not start, or it started and did not finish. `status` is ran | scratched |
989
1333
  * late_scratching | reserve (an emergency that never got a start). `abnormal` is the official
990
- * reason cell, and it carries TWO families that settle in OPPOSITE directions, so read it with
991
- * `status` and never alone. (1) A DID-NOT-FINISH reason on a row whose `status` is `ran` —
992
- * Fell, PulledUp, Disqualified, TailedOff, StayedInBox: the runner started and simply has no
993
- * finishing position, and a bet on it lost. (2) A DID-NOT-START reason on a row whose `status`
994
- * is `scratched` — 'SB', Racing Australia's code for scratched at the barrier: the runner was
995
- * taken out of the market before the jump, so `position` is null, it is in no `placings`
996
- * entry, and bets on it were refunded while the rest of the race was deducted. An 'SB' row may
997
- * still carry `sp` — the last price Racing Australia printed for it, kept rather than blanked
998
- * because it is a real market fact about the withdrawn runner. It is NOT the basis of the
999
- * deduction, and you must never derive one from the other: `sp` comes from RA's results table
1000
- * while a deduction is struck by the settling bookmaker, and they do not agree. Measured
1001
- * 2026-09-08 across all five stored 'SB' rows that carry a price, 1/`sp` equals the published
1002
- * win deduction on NONE of them — Varejao, Sunshine Coast R4 2026-09-06, saddlecloth 5,
1003
- * withdrawn about 2 minutes 20 before the jump: `sp` 3.70, so 1/3.70 is 0.27 in the dollar,
1334
+ * reason cell, passed through VERBATIM from the source — on AU thoroughbred it is Racing
1335
+ * Australia's own Finish cell whenever that cell is not a number. THERE IS NO CLOSED LIST OF
1336
+ * CODES and you should not write a match statement that assumes one: the source invents them,
1337
+ * and a code nobody here has seen before will reach you unchanged rather than be dropped or
1338
+ * renamed. Everything stored to date, with counts as at 2026-09-14: Fell (101), TailedOff
1339
+ * (32), SB (25), PulledUp (8), FF (1, Rosehill R4 on 2026-09-12), LP (1). Treat `abnormal` as
1340
+ * the authoritative explanation for a missing `position`, and treat an unfamiliar value as a
1341
+ * did-not-finish unless `status` says otherwise — which is exactly what this API does: only
1342
+ * codes PROVEN to mean the runner never left the barrier flip `status` to `scratched`, and
1343
+ * that list currently holds one entry, 'SB'. Anything else stays `ran`, deliberately, because
1344
+ * misreading a starter as a non-starter turns a losing bet into a refund. It carries TWO
1345
+ * families that settle in OPPOSITE directions, so read it with `status` and never alone. (1) A
1346
+ * DID-NOT-FINISH reason on a row whose `status` is `ran` — Fell, PulledUp, Disqualified,
1347
+ * TailedOff, StayedInBox, FF and LP are the ones seen so far: the runner started and simply
1348
+ * has no finishing position, and a bet on it lost. (2) A DID-NOT-START reason on a row whose
1349
+ * `status` is `scratched` — 'SB', Racing Australia's code for scratched at the barrier: the
1350
+ * runner was taken out of the market before the jump, so `position` is null, it is in no
1351
+ * `placings` entry, and bets on it were refunded while the rest of the race was deducted. An
1352
+ * 'SB' row may still carry `sp` — the last price Racing Australia printed for it, kept rather
1353
+ * than blanked because it is a real market fact about the withdrawn runner. It is NOT the
1354
+ * basis of the deduction, and you must never derive one from the other: `sp` comes from RA's
1355
+ * results table while a deduction is struck by the settling bookmaker, and they do not agree.
1356
+ * Measured 2026-09-08 across all five stored 'SB' rows that carry a price, 1/`sp` equals the
1357
+ * published win deduction on NONE of them — Varejao, Sunshine Coast R4 2026-09-06, saddlecloth
1358
+ * 5, withdrawn about 2 minutes 20 before the jump: `sp` 3.70, so 1/3.70 is 0.27 in the dollar,
1004
1359
  * against a published `deductions` row of 0.18 win and 0.15 place; Dubai Dancer, Warrnambool
1005
1360
  * R5: 1/5.50 is 0.18 against a published 0.10. Settle from `deductions`, which is
1006
1361
  * authoritative. None of the five carries a `position` or a `margin`. Telling a barrier
@@ -1012,33 +1367,66 @@ export interface RaceResultOut {
1012
1367
  * barrier it jumped from — RA prints 0 there and we do not pass that sentinel on), and an
1013
1368
  * ordinary scratching carries `sp` null while an 'SB' may carry one. So `abnormal == 'SB'`
1014
1369
  * identifies a barrier scratching; `abnormal` null tells you only that it was not one, not how
1015
- * early it happened — use `deductions[].scratched_at` for that. On greyhound (Topaz)
1016
- * `abnormal` is null on every scratching and `status` is the discriminator: the LATE one is
1017
- * `late_scratching`, an earlier one is `scratched`. So today `late_scratching` is a
1018
- * greyhound-only value — a thoroughbred barrier scratching does NOT use it — and `scratched`
1019
- * does not carry identical meaning in the two codes. `deductions` carries `scratched_at` for
1020
- * either, and is the one cross-code way to see how close to the jump a runner came out.
1021
- * `dead_heat` is true on each runner sharing a position. `runner_ref` is a STABLE runner
1022
- * identifier from the official registry (namespaced: ra:<code> thoroughbred, grv:<id>
1023
- * greyhound) — it persists across meetings, so it is the join key for longitudinal work; null
1024
- * while a code's registry source is not yet wired. `trainer` and `jockey` come from the same
1025
- * official results source and may be null where that source is not yet live for the code. `sp`
1026
- * is the OFFICIAL STARTING PRICE as a decimal return on a $1 stake (4.6 means $4.60), null
1027
- * where it is unknown — never 0. It is the settling market's own price, not our consensus and
1028
- * not a bookmaker's fixed price: greyhound SP comes from the official Topaz feed, thoroughbred
1029
- * SP from the Racing Australia results table, and HARNESS SP IS ALWAYS NULL — no source we
1030
- * hold publishes it. `sp_note` carries Racing Australia's favouritism marker on thoroughbred
1031
- * runners ('F' favourite, 'EF' equal favourite) and is null everywhere else. Coverage
1032
- * RE-MEASURED 2026-09-08 over the whole retained store, counting only rows whose `status` is
1033
- * `ran` (a barrier scratching is not a starter and its price is not in this denominator):
1034
- * 99.8% of greyhound starters carry `sp` (14,540 of 14,568) and 88.6% of thoroughbred starters
1035
- * do (2,105 of 2,375). The Racing Australia block that held thoroughbred SP at 0% when this
1036
- * was last measured on 2026-09-03 has lifted and the collector has been landing meetings
1037
- * since. A null `sp` is unknown, never zero — RA prints no SP at all on some rows. Null
1038
- * `runners` means the full field is not available for this race — never an empty field;
1039
- * population is rolling out per racing code as our own results collection comes online.
1040
- * `placings` remains the source for the settling bookmaker's own fixed prices, and now also
1041
- * carries `sp` copied from here.
1370
+ * early it happened — use `deductions[].scratched_at` for that. On greyhound (our own
1371
+ * collection since 2026-09-13: FastTrack for Victorian tracks, the national results pages for
1372
+ * every other state) `abnormal` is null on every scratching and `status` carries three values:
1373
+ * `ran`, `scratched` (merged in from our own `deductions`, so it never says how late), and
1374
+ * `reserve` for a rug 9/10 reserve that never got a box. `late_scratching` was the retired
1375
+ * interim feed's greyhound-only value — a thoroughbred barrier scratching does NOT use it —
1376
+ * and it is no longer emitted; `scratched` does not carry identical meaning in the two codes.
1377
+ * `deductions` carries `scratched_at` for either, and is the one cross-code way to see how
1378
+ * close to the jump a runner came out. `dead_heat` is true on each runner sharing a position.
1379
+ * IDENTITY, CORRECTED 2026-09-21. `runner_ref` (`ra:<code>`) is Racing Australia's code for
1380
+ * this ENTRY: it joins the runner to RA's form page for that meeting and to our acceptance and
1381
+ * field rows for the same day, and it changes from meeting to meeting — measured 2026-09-21,
1382
+ * 1,450 of the 1,452 horses with two or more runs in our own settled results carried a
1383
+ * DIFFERENT code on every run. It is not a horse identity, and this description said it was
1384
+ * until today. `horse_ref` (`pe:<name>`) is: the registered name folded to letters and digits
1385
+ * (trailing country parenthetical off), the same value on results, the live board, the
1386
+ * closing-line and price-path archives, /v1/racing/horses/form, /v1/racing/horses/runs and the
1387
+ * backfill, and it is the key to join a horse across meetings. It is derived at serve time
1388
+ * from the name on the row, never stored, so every historical row carries it; null on
1389
+ * greyhound and harness runners. On GREYHOUNDS `runner_ref` IS the dog: `grv:<id>` for
1390
+ * Victorian tracks as FastTrack numbers the dog and `grsa:<id>` elsewhere as the national
1391
+ * results pages number it — per-dog registry ids that DO persist across meetings, in two
1392
+ * namespaces, so a dog racing in both is two refs until mapped. `runner_ref` is null while a
1393
+ * code's registry source is not yet wired. `trainer` and `jockey` come from the same official
1394
+ * results source and may be null where that source is not yet live for the code.
1395
+ * GREYHOUND-ONLY, ADDITIVE FROM 2026-09-21, five measured columns the results pages print and
1396
+ * this API used to drop. `first_split_s` is the first sectional in seconds exactly as the
1397
+ * source page printed it, null where the page printed none. `second_split_s` is the second
1398
+ * sectional, and is null on EVERY Victorian run because FastTrack publishes one split and not
1399
+ * two. `weight_kg` is the greyhound's race-day weight in kilograms, null where the page
1400
+ * carries no weight column at all — Western Australian meetings carry none (0 of 36 runner
1401
+ * rows sampled 2026-09-21). `pir` is FastTrack's position-in-running text passed through as a
1402
+ * STRING and never a number ("21" means 2nd at the first mark then 1st at the second),
1403
+ * Victorian runs only and null everywhere else. `grade` is the race's grade in the SOURCE'S
1404
+ * OWN WORDS — "M", "4/5" or "FFA" on the national pages, a race type such as "Tier 3 - Maiden"
1405
+ * on FastTrack — and the two vocabularies are NOT mapped onto each other, so do not compare a
1406
+ * Victorian grade with an interstate one. ⚠️ A STORED FIELD IS NEVER REWRITTEN, so a greyhound
1407
+ * field written before 2026-09-21 carries none of these five keys AT ALL — absent, not null —
1408
+ * and no thoroughbred field carries them in any era: read them with `.get()`. Measured on 46
1409
+ * freshly read race pages on 2026-09-21, a grade appeared on 46/46 races and 362/362 runner
1410
+ * rows, a first split on 288/362 and a weight on 295/362; the rows without them are
1411
+ * overwhelmingly rug 9/10 reserves, which never started and for which the page prints nothing.
1412
+ * `sp` is the OFFICIAL STARTING PRICE as a decimal return on a $1 stake (4.6 means $4.60),
1413
+ * null where it is unknown — never 0. It is the settling market's own price, not our consensus
1414
+ * and not a bookmaker's fixed price: thoroughbred SP comes from the Racing Australia results
1415
+ * table, greyhound SP from our own collection of published results (Victorian tracks via
1416
+ * FastTrack, every other state via the national results pages, both from 2026-09-13), and
1417
+ * HARNESS SP IS ALWAYS NULL — no source we hold publishes it. `sp_note` carries Racing
1418
+ * Australia's favouritism marker on thoroughbred runners ('F' favourite, 'EF' equal favourite)
1419
+ * and is null everywhere else. Coverage RE-MEASURED 2026-09-08 over the whole retained store,
1420
+ * counting only rows whose `status` is `ran` (a barrier scratching is not a starter and its
1421
+ * price is not in this denominator): 88.6% of thoroughbred starters carry `sp` (2,105 of
1422
+ * 2,375); on the greyhound races collected since 2026-09-13, 2,054 of 2,077 Victorian starters
1423
+ * do (98.9%) and 4233 of 4596 of the other states' starters do (92.1%), both measured
1424
+ * 2026-09-20. The Racing Australia block that held thoroughbred SP at 0% when this was last
1425
+ * measured on 2026-09-03 has lifted and the collector has been landing meetings since. A null
1426
+ * `sp` is unknown, never zero — RA prints no SP at all on some rows. Null `runners` means the
1427
+ * full field is not available for this race — never an empty field; AU thoroughbred and AU
1428
+ * greyhound races carry one. `placings` remains the source for the settling bookmaker's own
1429
+ * fixed prices, and now also carries `sp` copied from here.
1042
1430
  */
1043
1431
  runners?: Record<string, unknown>[] | null;
1044
1432
  /**
@@ -1050,35 +1438,38 @@ export interface RaceResultOut {
1050
1438
  * this race has not been yet; `field_note` names the UTC instant by which it will have run.
1051
1439
  * `overdue`: that pass has been and gone without writing a field, but the collector still
1052
1440
  * selects this row as a candidate and keeps re-attempting it — this state is NOT terminal. For
1053
- * AU greyhound the Topaz enricher re-reads every field-less row on each daily run for ten days
1054
- * after the race (its own LOOKBACK_DAYS); for AU thoroughbred a bounded re-fetch reaches six
1055
- * days but is run by hand rather than on a schedule. Most rows that reach `overdue` stay null,
1056
- * so do not build on a field arriving — but do not write the race off either, and `field_note`
1057
- * gives the horizon. `unavailable`: no scheduled pass still selects this row. Its collector's
1058
- * own candidate window closed at the instant in `field_note` and no field was written — the
1059
- * pass either never matched the race or refused an ambiguous match, which it does rather than
1060
- * write another race's field. `unsupported`: no collector has ever existed for this
1061
- * category/country — AU harness, NZ thoroughbred and NZ harness, 1,254 of the 5,577 stored
1062
- * results on 2026-09-10, plus any other country that appears because an AU book listed the
1063
- * meeting — and no amount of waiting will produce one. `not_applicable`: the race did not run
1064
- * (abandoned, postponed or transferred) and carries no field, so there is nothing to collect.
1065
- * TRANSITIONS, stated explicitly because acting on the wrong one costs you data. The scheduled
1066
- * progression is pending -> overdue -> unavailable, one direction only. It is bounded at every
1067
- * step by a collector's own window rather than by hope: a race can sit
1068
- * matched-and-never-resulted forever (The Gardens, 2026-09-05, 12 races), and an open-ended
1069
- * `pending` would have you polling it forever. Any of those three becomes `complete` the
1070
- * moment a field is written, AND THAT INCLUDES `unavailable` — nothing un-writes a `runners`
1071
- * array, so a collector re-run by hand, a widened window or a late upstream publication can
1072
- * still fill a row we have stopped scheduling passes for. So `unavailable` means 'nothing
1073
- * further is scheduled', never 'this can never arrive'; if you reconcile, re-read rather than
1074
- * caching it as final. `unsupported` and `not_applicable` are properties of the racing code
1075
- * and of the race itself, not of a schedule, and do not change on their own.
1441
+ * AU greyhound the hourly pass for its state (FastTrack for Victoria, the national results
1442
+ * pages elsewhere) re-reads every field-less race inside its seven-day window; for AU
1443
+ * thoroughbred a bounded re-fetch reaches six days but is run by hand rather than on a
1444
+ * schedule. Most rows that reach `overdue` stay null, so do not build on a field arriving —
1445
+ * but do not write the race off either, and `field_note` gives the horizon. `unavailable`: no
1446
+ * scheduled pass still selects this row. Its collector's own candidate window closed at the
1447
+ * instant in `field_note` and no field was written — the pass either never matched the race or
1448
+ * refused an ambiguous match, which it does rather than write another race's field.
1449
+ * `unsupported`: no collector has ever existed for this category/country — AU harness, NZ
1450
+ * thoroughbred and NZ harness, 1,254 of the 5,577 stored results on 2026-09-10, plus any other
1451
+ * country that appears because an AU book listed the meeting — and no amount of waiting will
1452
+ * produce one. `not_applicable`: the race did not run (abandoned, postponed or transferred)
1453
+ * and carries no field, so there is nothing to collect. TRANSITIONS, stated explicitly because
1454
+ * acting on the wrong one costs you data. The scheduled progression is pending -> overdue ->
1455
+ * unavailable, one direction only. It is bounded at every step by a collector's own window
1456
+ * rather than by hope: a race can sit matched-and-never-resulted forever (The Gardens,
1457
+ * 2026-09-05, 12 races), and an open-ended `pending` would have you polling it forever. Any of
1458
+ * those three becomes `complete` the moment a field is written, AND THAT INCLUDES
1459
+ * `unavailable` — nothing un-writes a `runners` array, so a collector re-run by hand, a
1460
+ * widened window or a late upstream publication can still fill a row we have stopped
1461
+ * scheduling passes for. So `unavailable` means 'nothing further is scheduled', never 'this
1462
+ * can never arrive'; if you reconcile, re-read rather than caching it as final. `unsupported`
1463
+ * and `not_applicable` are properties of the racing code and of the race itself, not of a
1464
+ * schedule, and do not change on their own.
1076
1465
  */
1077
1466
  field_status?: string;
1078
1467
  /**
1079
- * Which collector owns `runners` for this race: `racing_australia` (AU thoroughbred), `topaz`
1080
- * (AU greyhound), or null where no collector exists. Set on `pending`, `overdue`,
1081
- * `unavailable` and `complete` rows alike — it names who WOULD fill the field, not who did.
1468
+ * Which collector owns `runners` for this race: `racing_australia` (AU thoroughbred),
1469
+ * `fasttrack` (AU greyhound at Victorian tracks) and `grsa` (AU greyhound everywhere else),
1470
+ * the last two our own collection, or null where no collector exists. Set on `pending`,
1471
+ * `overdue`, `unavailable` and `complete` rows alike — it names who WOULD fill the field, not
1472
+ * who did.
1082
1473
  */
1083
1474
  field_source?: string | null;
1084
1475
  /**
@@ -1282,6 +1673,21 @@ export interface ResultsCoverageOut {
1282
1673
  /** Prose: which codes are collected, from when, and what the never-collected codes are. */
1283
1674
  field_scope: string;
1284
1675
  }
1676
+ export interface RewardIn {
1677
+ /** `cash` or `credits`. */
1678
+ reward_type: string;
1679
+ /**
1680
+ * Where a cash remittance should go, if it differs from the account email. Ignored for
1681
+ * `credits`.
1682
+ */
1683
+ payout_email?: string | null;
1684
+ /**
1685
+ * Requested referral code, first call only. 3-24 characters, lowercase letters, digits and
1686
+ * hyphens. Ignored once a code has been issued — a code that changes is a dead link on
1687
+ * somebody's blog.
1688
+ */
1689
+ code?: string | null;
1690
+ }
1285
1691
  export interface RunPIR {
1286
1692
  /** Metres from the finish, e.g. 800. */
1287
1693
  at_m: number;
@@ -1290,19 +1696,65 @@ export interface RunPIR {
1290
1696
  export interface RunPlacegetter {
1291
1697
  position: number;
1292
1698
  name: string;
1293
- /** That horse's own stable ra: code — walkable straight back into this endpoint. */
1699
+ /**
1700
+ * That horse's own ra: code for that meeting — walkable straight back into this endpoint,
1701
+ * though it is the entry code and not a horse identity.
1702
+ */
1294
1703
  runner_ref?: string | null;
1295
1704
  weight_kg?: number | null;
1296
1705
  }
1706
+ export interface RunsSourceBlock {
1707
+ /**
1708
+ * Where the data comes from: our own collection of published AU thoroughbred results (Racing
1709
+ * Australia's results pages, read hourly by our results collector), flattened to one row per
1710
+ * horse per race and refreshed hourly. Not Racing Australia's form page — that is
1711
+ * /v1/racing/horses/form.
1712
+ */
1713
+ feed?: string;
1714
+ region?: string;
1715
+ sport?: string;
1716
+ /**
1717
+ * The first complete card in the store. A horse's record here starts here — it is not a
1718
+ * career.
1719
+ */
1720
+ coverage_from?: string;
1721
+ /** Sydney date of the latest race in the store. */
1722
+ coverage_to?: string | null;
1723
+ /**
1724
+ * Which of our collectors can contribute a run. AU thoroughbred results come from one: the
1725
+ * Racing Australia results pages.
1726
+ */
1727
+ collectors?: string[];
1728
+ /** The store is rebuilt from our own results at :59 each hour, after the results pass. */
1729
+ refreshed_hourly?: boolean;
1730
+ }
1297
1731
  export interface SourceBlock {
1732
+ /**
1733
+ * Where the data comes from: our own collection of published AU greyhound results, Victorian
1734
+ * tracks via FastTrack (runner_ref grv:) and every other state via the national results pages
1735
+ * (runner_ref grsa:). Runs on or after `results_from` are races we settled ourselves; earlier
1736
+ * runs are the same pages read back by our history collector. Refreshed hourly.
1737
+ */
1298
1738
  feed?: string;
1299
1739
  region?: string;
1300
1740
  sport?: string;
1301
- coverage_from: string;
1302
- /** Meeting date of the latest run held, i.e. how far the last sync got. */
1741
+ /** Sydney date of the OLDEST run in the store. A dog's record here starts here. */
1742
+ coverage_from?: string;
1743
+ /**
1744
+ * Sydney date our own settled results begin. A run on or after it has `origin: results` and a
1745
+ * `result_id` that joins to /v1/racing/results; an earlier one has `origin: history` and does
1746
+ * not.
1747
+ */
1748
+ results_from?: string;
1749
+ /** Sydney date of the latest race in the store. */
1303
1750
  coverage_to?: string | null;
1304
- /** UTC timestamp of the last Topaz sync into this store. */
1305
- synced_at?: string | null;
1751
+ /**
1752
+ * Which of our collectors can contribute a run: `fasttrack` = Victorian tracks, `grsa` = every
1753
+ * other state.
1754
+ */
1755
+ collectors?: string[];
1756
+ /** The store is rebuilt from our results at :58 each hour, after both collectors have written. */
1757
+ refreshed_hourly?: boolean;
1306
1758
  }
1307
1759
  export interface SplitRecord {
1308
1760
  starts: number;
@@ -1352,16 +1804,104 @@ export interface SportsArbOut {
1352
1804
  max_overlay_pct: number;
1353
1805
  selections: Record<string, unknown>[];
1354
1806
  }
1807
+ export interface TeamKeyIn {
1808
+ /**
1809
+ * What this key is for — 'results worker', 'Jo's laptop'. Shown in the pool view and in your
1810
+ * usage breakdown.
1811
+ */
1812
+ label: string;
1813
+ /**
1814
+ * Optional. The most credits THIS key may spend per month, hard, inside the account's shared
1815
+ * pool — between 1 and the plan's own monthly allowance. Omit it (or send null) and the key
1816
+ * may spend the whole pool. Caps reset with the pool on the 1st.
1817
+ */
1818
+ monthly_cap?: number | null;
1819
+ }
1820
+ export interface TeamKeyListOut {
1821
+ plan: string;
1822
+ allowance: number;
1823
+ keys_in_use: number;
1824
+ pool_credits_used: number;
1825
+ pool_credits_limit: number;
1826
+ pool_credits_remaining: number;
1827
+ max_monthly_cap?: number;
1828
+ primary: TeamKeyOut;
1829
+ team: TeamKeyOut[];
1830
+ }
1831
+ export interface TeamKeyMintOut {
1832
+ key_id: string;
1833
+ /**
1834
+ * The team key. Shown ONCE; it is stored hashed and cannot be recovered — only revoked and
1835
+ * re-minted.
1836
+ */
1837
+ api_key: string;
1838
+ label: string;
1839
+ plan: string;
1840
+ /** Keys this account may hold, primary included. */
1841
+ allowance: number;
1842
+ keys_in_use: number;
1843
+ pool_credits_limit: number;
1844
+ pool_credits_used: number;
1845
+ monthly_cap?: number | null;
1846
+ /** The largest cap this plan accepts — its own monthly allowance. */
1847
+ max_monthly_cap?: number;
1848
+ }
1849
+ export interface TeamKeyOut {
1850
+ key_id: string;
1851
+ key_hint: string;
1852
+ label: string | null;
1853
+ plan: string;
1854
+ created_at?: string | null;
1855
+ last_used_at?: string | null;
1856
+ period_calls?: number;
1857
+ period_credits?: number;
1858
+ /** This key's hard per-month credit cap, or null for no cap. */
1859
+ monthly_cap?: number | null;
1860
+ /**
1861
+ * Credits charged against this key's own counter this period. That is the number the cap is
1862
+ * checked against; it stays 0 on an uncapped team key, whose spending is only ever charged to
1863
+ * the pool row. Use period_credits for what an uncapped key has spent.
1864
+ */
1865
+ key_credits_used?: number;
1866
+ }
1867
+ /**
1868
+ * Both fields are optional; only the ones PRESENT in the body are written. `monthly_cap: null`
1869
+ * is meaningful (remove the cap) and must not read as "unchanged", so the handler asks
1870
+ * model_fields_set which keys the caller actually sent rather than inferring intent from a
1871
+ * None that pydantic would produce either way.
1872
+ */
1873
+ export interface TeamKeyPatchIn {
1874
+ /** New label for this key. Omit to leave it alone. */
1875
+ label?: string | null;
1876
+ /**
1877
+ * Optional. The most credits THIS key may spend per month, hard, inside the account's shared
1878
+ * pool — between 1 and the plan's own monthly allowance. Omit it (or send null) and the key
1879
+ * may spend the whole pool. Caps reset with the pool on the 1st. Send null to REMOVE an
1880
+ * existing cap; omit the field entirely to leave the cap as it is.
1881
+ */
1882
+ monthly_cap?: number | null;
1883
+ }
1355
1884
  export interface TrackConditionsOut {
1356
1885
  /** Meeting day, Australia/Sydney calendar date. */
1357
1886
  date: string;
1358
1887
  meetings: Record<string, unknown>[];
1359
1888
  note: string;
1360
1889
  }
1361
- export interface TrainerRef {
1362
- /** Topaz trainerId. Stable across meetings. */
1363
- id?: number | null;
1364
- name?: string | null;
1890
+ export interface UpstreamBlock {
1891
+ /**
1892
+ * `ok` when Racing Australia is answering form reads, `walled` when it is serving its
1893
+ * bot-protection challenge to every fresh read and nothing can be collected.
1894
+ */
1895
+ state: string;
1896
+ /**
1897
+ * UTC time of the first refusal in the current streak, null when `state` is `ok`. This is the
1898
+ * field that tells you a stalled queue is hours old rather than minutes.
1899
+ */
1900
+ walled_since?: string | null;
1901
+ /** How long the current streak has run, in hours, null when `state` is `ok`. */
1902
+ walled_hours?: number | null;
1903
+ /** One sentence saying what that means for this job, in plain words. */
1904
+ note: string;
1365
1905
  }
1366
1906
  export interface UsageOut {
1367
1907
  plan: string;
@@ -1383,6 +1923,9 @@ export interface UsageOut {
1383
1923
  recent_activity?: Record<string, unknown>[];
1384
1924
  usage_by_endpoint_period?: Record<string, unknown>[];
1385
1925
  recent_activity_period?: Record<string, unknown>[];
1926
+ pool?: Record<string, unknown> | null;
1927
+ overage?: Record<string, unknown>;
1928
+ credits_remaining_with_overage?: number;
1386
1929
  plans?: Record<string, Record<string, unknown>>;
1387
1930
  }
1388
1931
  export interface VenueOut {