songbrain 0.1.2 → 0.3.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.
package/dist/index.d.cts CHANGED
@@ -124,11 +124,27 @@ interface LyricLine {
124
124
  start: number;
125
125
  end: number;
126
126
  text: string;
127
+ /** 0-1 transcription confidence; null when the words were provided. */
128
+ confidence?: number | null;
127
129
  /** Omitted in view=summary. */
128
130
  words?: Word[];
129
131
  }
132
+ interface LyricsAlignment {
133
+ /** Share of the provided words that were found in the audio. */
134
+ match_ratio?: number | null;
135
+ reference_words?: number | null;
136
+ corrected_words?: number | null;
137
+ /** false when the provided lyrics did not match the audio well enough. */
138
+ used?: boolean;
139
+ reason?: string;
140
+ }
130
141
  interface Lyrics {
131
142
  language?: string | null;
143
+ /** Where the words come from. */
144
+ source?: "transcribed" | "provided_lyrics" | "suno_lyrics";
145
+ basis?: "transcribed" | "provided";
146
+ note?: string;
147
+ alignment?: LyricsAlignment;
132
148
  lines?: LyricLine[];
133
149
  /** true for recognised commercial recordings; then `lines` is absent. */
134
150
  hidden?: boolean;
@@ -139,6 +155,17 @@ interface ScoreBlock {
139
155
  score: number;
140
156
  breakdown: Record<string, number>;
141
157
  note?: string;
158
+ /** "model_estimate": an AI model's listening judgement, not a measurement. */
159
+ basis?: "model_estimate";
160
+ }
161
+ /** Which fields are measured from the audio and which are a model's reading. */
162
+ interface Provenance {
163
+ measured: string[];
164
+ transcribed: string[];
165
+ model_estimate: string[];
166
+ generated: string[];
167
+ confidence_fields: string[];
168
+ doc: string;
142
169
  }
143
170
  interface WhatWorks {
144
171
  title: string;
@@ -158,6 +185,8 @@ interface Scores {
158
185
  what_works: WhatWorks[];
159
186
  what_to_fix: WhatToFix[];
160
187
  audience: string[];
188
+ basis?: "model_estimate";
189
+ basis_note?: string;
161
190
  }
162
191
  interface StoryElement {
163
192
  /** The one object the video is about. */
@@ -273,6 +302,8 @@ interface ShotPlan {
273
302
  interface ErrorInfo {
274
303
  code: string;
275
304
  message: string;
305
+ /** "req_…", also in the Songbrain-Request-Id header. */
306
+ request_id?: string;
276
307
  }
277
308
  /**
278
309
  * The song document. While processing, only `id`, `status`, `progress` and
@@ -298,6 +329,10 @@ interface Song {
298
329
  created_at?: string | null;
299
330
  external_ref?: string | null;
300
331
  billing?: Billing;
332
+ /** false for test-mode songs. */
333
+ livemode?: boolean;
334
+ /** Measured vs. transcribed vs. model estimate vs. generated. */
335
+ provenance?: Provenance;
301
336
  }
302
337
  /** The 202 body of POST /songs. */
303
338
  interface SongCreated {
@@ -308,6 +343,7 @@ interface SongCreated {
308
343
  billing: Billing;
309
344
  external_ref?: string | null;
310
345
  url: string;
346
+ livemode?: boolean;
311
347
  }
312
348
  /** GET /songs/{id}/shot-plan and GET /examples/{id}/shot-plan. */
313
349
  interface SongShotPlan {
@@ -323,10 +359,14 @@ interface SongListItem {
323
359
  external_ref: string | null;
324
360
  created_at: string | null;
325
361
  billing: Billing;
362
+ livemode?: boolean;
326
363
  }
327
364
  interface SongList {
328
365
  object: "list";
329
366
  data: SongListItem[];
367
+ has_more?: boolean;
368
+ /** Pass as `startingAfter` for the next page. */
369
+ next_cursor?: string | null;
330
370
  }
331
371
  interface DeleteResult {
332
372
  id: string;
@@ -383,11 +423,44 @@ interface Pricing {
383
423
  }
384
424
  /** A webhook event body. */
385
425
  interface WebhookEvent<T = Record<string, unknown>> {
386
- type: "song.done" | "song.failed" | "account.low_balance" | (string & {});
426
+ /** "evt_…". Stays the same across retries of one event: dedupe on it. */
427
+ id: string;
428
+ type: "song.done" | "song.failed" | "account.low_balance" | "ping" | (string & {});
387
429
  /** Unix seconds. */
388
430
  created: number;
431
+ livemode: boolean;
389
432
  data: T;
390
433
  }
434
+ /** From the X-RateLimit-* headers. See `Songbrain.lastRateLimit`. */
435
+ interface RateLimitInfo {
436
+ /** Requests per minute. */
437
+ limit: number;
438
+ remaining: number | null;
439
+ /** Seconds until the window has room again. */
440
+ reset: number | null;
441
+ }
442
+ /** POST /webhooks/test. */
443
+ interface WebhookTestResult {
444
+ delivered: boolean;
445
+ status_code: number | null;
446
+ latency_ms: number;
447
+ event_id: string;
448
+ }
449
+ interface WebhookDelivery {
450
+ event_id: string;
451
+ type: WebhookEvent["type"];
452
+ song_id: string | null;
453
+ url: string;
454
+ attempt: number;
455
+ status_code: number | null;
456
+ delivered: boolean;
457
+ latency_ms: number | null;
458
+ created_at: string;
459
+ }
460
+ interface WebhookDeliveryList {
461
+ object: "list";
462
+ data: WebhookDelivery[];
463
+ }
391
464
 
392
465
  declare const DEFAULT_BASE_URL = "https://api.songbrain.ai/v1";
393
466
  /** A Blob/File, a Buffer or bytes, or a local file path (Node). */
@@ -399,7 +472,10 @@ interface SongbrainOptions {
399
472
  baseUrl?: string;
400
473
  /** Timeout for each HTTP request in ms (uploads included). Default 60 000. */
401
474
  timeoutMs?: number;
402
- /** How often to retry 429 and 5xx responses, with backoff. Default 3. */
475
+ /**
476
+ * How often to retry network errors, 429 and 5xx responses, with backoff. Default 3.
477
+ * Song creation is retried too, because every create sends an `Idempotency-Key`.
478
+ */
403
479
  maxRetries?: number;
404
480
  /** A custom fetch implementation. Defaults to the global fetch. */
405
481
  fetch?: typeof fetch;
@@ -417,6 +493,25 @@ interface AnalyzeOptions {
417
493
  externalRef?: string;
418
494
  /** File name for an upload. Its extension must match the audio format. Taken from the path or File name, or guessed from the bytes. */
419
495
  filename?: string;
496
+ /**
497
+ * The song's lyrics as plain text (up to 20,000 characters), e.g. from Suno. The
498
+ * analysis then uses your exact words on the transcription's timing
499
+ * (`lyrics.source` = "provided_lyrics"); words the singer can't be heard on are
500
+ * left out, never guessed.
501
+ */
502
+ lyrics?: string;
503
+ /**
504
+ * Test mode: free, never charged, no audio needed. The song is done right away and
505
+ * returns the Sugar Rush example analysis with `livemode: false`. `song.done` is
506
+ * still sent to `webhookUrl`. Made for CI and integration tests.
507
+ */
508
+ test?: boolean;
509
+ /**
510
+ * Sent as `Idempotency-Key`. By default a random UUID per call, reused across the
511
+ * client's own retries, so a retried create never makes a second song. Pass your
512
+ * own (e.g. your job id) to make retries across processes safe too (24 h window).
513
+ */
514
+ idempotencyKey?: string;
420
515
  /** Wait until the song is done and return the full document (default true). */
421
516
  wait?: boolean;
422
517
  /** Time between status checks while waiting. Default 5 000. */
@@ -428,6 +523,18 @@ interface WaitOptions {
428
523
  pollIntervalMs?: number;
429
524
  timeoutMs?: number;
430
525
  }
526
+ interface ListSongsOptions {
527
+ /** Page size, 1-100. Default 20. */
528
+ limit?: number;
529
+ /** A song id, usually the previous page's `next_cursor`. Returns the songs after it. */
530
+ startingAfter?: string;
531
+ }
532
+ interface IterSongsOptions {
533
+ /** Songs per request, 1-100. Default 100. */
534
+ pageSize?: number;
535
+ /** Start after this song id. */
536
+ startingAfter?: string;
537
+ }
431
538
  interface GetSongOptions {
432
539
  /** "summary" drops word timings and beat arrays (about 3x smaller). */
433
540
  view?: "full" | "summary";
@@ -450,11 +557,19 @@ declare class Songbrain {
450
557
  readonly timeoutMs: number;
451
558
  readonly maxRetries: number;
452
559
  private readonly fetchImpl;
560
+ /**
561
+ * `{ limit, remaining, reset }` from the `X-RateLimit-*` headers of the last response
562
+ * that had them (null before that). `reset` is seconds until the window has room.
563
+ */
564
+ lastRateLimit: RateLimitInfo | null;
565
+ /** The `Songbrain-Request-Id` of the last response (null before the first one). */
566
+ lastRequestId: string | null;
453
567
  /** @internal Overridable in tests. */
454
568
  _sleep: (ms: number) => Promise<void>;
455
569
  constructor(options?: SongbrainOptions);
456
570
  /**
457
- * Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`.
571
+ * Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`
572
+ * (or neither with `test: true`).
458
573
  * Typically 60-90 s. With `wait: true` (default) resolves to the full document;
459
574
  * with `wait: false` to the 202 body `{ id, status: "processing", eta_sec, billing }`.
460
575
  */
@@ -471,16 +586,31 @@ declare class Songbrain {
471
586
  getSong(songId: string, options?: GetSongOptions): Promise<Song>;
472
587
  /** Only the story and the shot plan: `{ id, status, song, story, shot_plan }`. */
473
588
  shotPlan(songId: string): Promise<SongShotPlan>;
474
- /** Your songs, newest first. `limit` is 1-100 (default 20). */
475
- listSongs(options?: {
476
- limit?: number;
477
- }): Promise<SongList>;
589
+ /** One page of your songs, newest first: `{ object: "list", data, has_more, next_cursor }`. */
590
+ listSongs(options?: ListSongsOptions): Promise<SongList>;
591
+ /**
592
+ * All your songs, newest first, fetching pages as needed.
593
+ *
594
+ * ```ts
595
+ * for await (const item of sb.iterSongs()) console.log(item.id, item.status);
596
+ * ```
597
+ */
598
+ iterSongs(options?: IterSongsOptions): AsyncGenerator<SongListItem, void, undefined>;
478
599
  /** Delete a song's audio and analysis now. 409 `still_processing` while it runs. */
479
600
  deleteSong(songId: string): Promise<DeleteResult>;
480
601
  /** Free songs left this month, credit balance and price per song. */
481
602
  account(): Promise<Account>;
482
603
  /** Prices and limits. No key needed. */
483
604
  pricing(): Promise<Pricing>;
605
+ /**
606
+ * Send a signed `ping` event to `url` right now:
607
+ * `{ delivered, status_code, latency_ms, event_id }`. Checks your receiver before real songs.
608
+ */
609
+ testWebhook(url: string): Promise<WebhookTestResult>;
610
+ /** The last webhook delivery attempts for your account, newest first. */
611
+ webhookDeliveries(options?: {
612
+ limit?: number;
613
+ }): Promise<WebhookDeliveryList>;
484
614
  /** Example analyses of Songbrain's own songs. No key needed. */
485
615
  examples(): Promise<ExampleList>;
486
616
  /** A full example document, in the same format as `getSong`. No key needed. */
@@ -492,9 +622,10 @@ declare class Songbrain {
492
622
  }
493
623
 
494
624
  /**
495
- * Every API error has the shape `{ error: { code, message } }`. The client
496
- * turns it into a `SongbrainError` (or a subclass) with `status`, `code` and
497
- * `message`.
625
+ * Every API error has the shape `{ error: { code, message, request_id } }`.
626
+ * The client turns it into a `SongbrainError` (or a subclass) with `status`,
627
+ * `code`, `message` and `requestId`. The message ends with
628
+ * ` (request_id: req_…)` when there is one; quote it when you contact support.
498
629
  */
499
630
  declare class SongbrainError extends Error {
500
631
  /** HTTP status, or 0 when the error did not come from an HTTP response. */
@@ -503,7 +634,9 @@ declare class SongbrainError extends Error {
503
634
  readonly code: string;
504
635
  /** The parsed response body, when there was one. */
505
636
  readonly body: unknown;
506
- constructor(status: number, code: string, message: string, body?: unknown);
637
+ /** The `Songbrain-Request-Id` of the failed request (`req_…`), or null. */
638
+ readonly requestId: string | null;
639
+ constructor(status: number, code: string, message: string, body?: unknown, requestId?: string | null);
507
640
  }
508
641
  /** 401: the API key is missing, invalid or revoked. */
509
642
  declare class AuthenticationError extends SongbrainError {
@@ -518,7 +651,7 @@ declare class NotFound extends SongbrainError {
518
651
  declare class RateLimited extends SongbrainError {
519
652
  /** Seconds to wait, from the Retry-After header (null if absent). */
520
653
  readonly retryAfter: number | null;
521
- constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null);
654
+ constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null, requestId?: string | null);
522
655
  }
523
656
  /** The song was accepted but its analysis failed. Paid credits are refunded automatically. */
524
657
  declare class AnalysisFailed extends SongbrainError {
@@ -546,13 +679,17 @@ type RawBody = string | Uint8Array | ArrayBuffer;
546
679
  declare function verifyWebhook(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): boolean;
547
680
  /**
548
681
  * Verify the signature and return the parsed event:
549
- * `{ type: "song.done" | "song.failed" | "account.low_balance", created, data }`.
682
+ * `{ id, type: "song.done" | "song.failed" | "account.low_balance" | "ping", created, livemode, data }`.
550
683
  * Throws if the signature is not valid.
684
+ *
685
+ * `id` (`evt_…`, also the `Songbrain-Event-Id` header) stays the same when
686
+ * Songbrain retries an event (up to 10 attempts over about 3 days): store the
687
+ * ids you have handled and skip repeats.
551
688
  */
552
689
  declare function constructWebhookEvent<T = Record<string, unknown>>(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): WebhookEvent<T>;
553
690
  /** Build a signature header value. Useful to test your webhook handler. */
554
691
  declare function signWebhook(rawBody: RawBody, secret: string, timestampSec?: number): string;
555
692
 
556
- declare const VERSION = "0.1.2";
693
+ declare const VERSION = "0.3.0";
557
694
 
558
- export { type Account, type Act, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, type Clip, DEFAULT_BASE_URL, type DeleteResult, type ErrorInfo, type ExampleList, type ExampleSummary, type FileInput, type FullSong, type FullSongSection, type GetSongOptions, type Instrument, InsufficientCredits, type KeyChange, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, NotFound, type Phases, type Pricing, RateLimited, SIGNATURE_HEADER, type Scene, type SceneKind, type ScoreBlock, type Scores, type Section, type SectionName, type ShotPlan, type ShotStyle, type Song, type SongCreated, type SongDNA, type SongInfo, type SongList, type SongListItem, type SongShotPlan, type SongStatus, Songbrain, SongbrainError, type SongbrainOptions, type SoundsLike, type Story, type StoryBeats, type StoryElement, type Timeline, VERSION, type VocalChange, type WaitOptions, WaitTimeout, type WebhookEvent, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };
695
+ export { type Account, type Act, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, type Clip, DEFAULT_BASE_URL, type DeleteResult, type ErrorInfo, type ExampleList, type ExampleSummary, type FileInput, type FullSong, type FullSongSection, type GetSongOptions, type Instrument, InsufficientCredits, type IterSongsOptions, type KeyChange, type ListSongsOptions, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, type LyricsAlignment, NotFound, type Phases, type Pricing, type Provenance, type RateLimitInfo, RateLimited, SIGNATURE_HEADER, type Scene, type SceneKind, type ScoreBlock, type Scores, type Section, type SectionName, type ShotPlan, type ShotStyle, type Song, type SongCreated, type SongDNA, type SongInfo, type SongList, type SongListItem, type SongShotPlan, type SongStatus, Songbrain, SongbrainError, type SongbrainOptions, type SoundsLike, type Story, type StoryBeats, type StoryElement, type Timeline, VERSION, type VocalChange, type WaitOptions, WaitTimeout, type WebhookDelivery, type WebhookDeliveryList, type WebhookEvent, type WebhookTestResult, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };
package/dist/index.d.ts CHANGED
@@ -124,11 +124,27 @@ interface LyricLine {
124
124
  start: number;
125
125
  end: number;
126
126
  text: string;
127
+ /** 0-1 transcription confidence; null when the words were provided. */
128
+ confidence?: number | null;
127
129
  /** Omitted in view=summary. */
128
130
  words?: Word[];
129
131
  }
132
+ interface LyricsAlignment {
133
+ /** Share of the provided words that were found in the audio. */
134
+ match_ratio?: number | null;
135
+ reference_words?: number | null;
136
+ corrected_words?: number | null;
137
+ /** false when the provided lyrics did not match the audio well enough. */
138
+ used?: boolean;
139
+ reason?: string;
140
+ }
130
141
  interface Lyrics {
131
142
  language?: string | null;
143
+ /** Where the words come from. */
144
+ source?: "transcribed" | "provided_lyrics" | "suno_lyrics";
145
+ basis?: "transcribed" | "provided";
146
+ note?: string;
147
+ alignment?: LyricsAlignment;
132
148
  lines?: LyricLine[];
133
149
  /** true for recognised commercial recordings; then `lines` is absent. */
134
150
  hidden?: boolean;
@@ -139,6 +155,17 @@ interface ScoreBlock {
139
155
  score: number;
140
156
  breakdown: Record<string, number>;
141
157
  note?: string;
158
+ /** "model_estimate": an AI model's listening judgement, not a measurement. */
159
+ basis?: "model_estimate";
160
+ }
161
+ /** Which fields are measured from the audio and which are a model's reading. */
162
+ interface Provenance {
163
+ measured: string[];
164
+ transcribed: string[];
165
+ model_estimate: string[];
166
+ generated: string[];
167
+ confidence_fields: string[];
168
+ doc: string;
142
169
  }
143
170
  interface WhatWorks {
144
171
  title: string;
@@ -158,6 +185,8 @@ interface Scores {
158
185
  what_works: WhatWorks[];
159
186
  what_to_fix: WhatToFix[];
160
187
  audience: string[];
188
+ basis?: "model_estimate";
189
+ basis_note?: string;
161
190
  }
162
191
  interface StoryElement {
163
192
  /** The one object the video is about. */
@@ -273,6 +302,8 @@ interface ShotPlan {
273
302
  interface ErrorInfo {
274
303
  code: string;
275
304
  message: string;
305
+ /** "req_…", also in the Songbrain-Request-Id header. */
306
+ request_id?: string;
276
307
  }
277
308
  /**
278
309
  * The song document. While processing, only `id`, `status`, `progress` and
@@ -298,6 +329,10 @@ interface Song {
298
329
  created_at?: string | null;
299
330
  external_ref?: string | null;
300
331
  billing?: Billing;
332
+ /** false for test-mode songs. */
333
+ livemode?: boolean;
334
+ /** Measured vs. transcribed vs. model estimate vs. generated. */
335
+ provenance?: Provenance;
301
336
  }
302
337
  /** The 202 body of POST /songs. */
303
338
  interface SongCreated {
@@ -308,6 +343,7 @@ interface SongCreated {
308
343
  billing: Billing;
309
344
  external_ref?: string | null;
310
345
  url: string;
346
+ livemode?: boolean;
311
347
  }
312
348
  /** GET /songs/{id}/shot-plan and GET /examples/{id}/shot-plan. */
313
349
  interface SongShotPlan {
@@ -323,10 +359,14 @@ interface SongListItem {
323
359
  external_ref: string | null;
324
360
  created_at: string | null;
325
361
  billing: Billing;
362
+ livemode?: boolean;
326
363
  }
327
364
  interface SongList {
328
365
  object: "list";
329
366
  data: SongListItem[];
367
+ has_more?: boolean;
368
+ /** Pass as `startingAfter` for the next page. */
369
+ next_cursor?: string | null;
330
370
  }
331
371
  interface DeleteResult {
332
372
  id: string;
@@ -383,11 +423,44 @@ interface Pricing {
383
423
  }
384
424
  /** A webhook event body. */
385
425
  interface WebhookEvent<T = Record<string, unknown>> {
386
- type: "song.done" | "song.failed" | "account.low_balance" | (string & {});
426
+ /** "evt_…". Stays the same across retries of one event: dedupe on it. */
427
+ id: string;
428
+ type: "song.done" | "song.failed" | "account.low_balance" | "ping" | (string & {});
387
429
  /** Unix seconds. */
388
430
  created: number;
431
+ livemode: boolean;
389
432
  data: T;
390
433
  }
434
+ /** From the X-RateLimit-* headers. See `Songbrain.lastRateLimit`. */
435
+ interface RateLimitInfo {
436
+ /** Requests per minute. */
437
+ limit: number;
438
+ remaining: number | null;
439
+ /** Seconds until the window has room again. */
440
+ reset: number | null;
441
+ }
442
+ /** POST /webhooks/test. */
443
+ interface WebhookTestResult {
444
+ delivered: boolean;
445
+ status_code: number | null;
446
+ latency_ms: number;
447
+ event_id: string;
448
+ }
449
+ interface WebhookDelivery {
450
+ event_id: string;
451
+ type: WebhookEvent["type"];
452
+ song_id: string | null;
453
+ url: string;
454
+ attempt: number;
455
+ status_code: number | null;
456
+ delivered: boolean;
457
+ latency_ms: number | null;
458
+ created_at: string;
459
+ }
460
+ interface WebhookDeliveryList {
461
+ object: "list";
462
+ data: WebhookDelivery[];
463
+ }
391
464
 
392
465
  declare const DEFAULT_BASE_URL = "https://api.songbrain.ai/v1";
393
466
  /** A Blob/File, a Buffer or bytes, or a local file path (Node). */
@@ -399,7 +472,10 @@ interface SongbrainOptions {
399
472
  baseUrl?: string;
400
473
  /** Timeout for each HTTP request in ms (uploads included). Default 60 000. */
401
474
  timeoutMs?: number;
402
- /** How often to retry 429 and 5xx responses, with backoff. Default 3. */
475
+ /**
476
+ * How often to retry network errors, 429 and 5xx responses, with backoff. Default 3.
477
+ * Song creation is retried too, because every create sends an `Idempotency-Key`.
478
+ */
403
479
  maxRetries?: number;
404
480
  /** A custom fetch implementation. Defaults to the global fetch. */
405
481
  fetch?: typeof fetch;
@@ -417,6 +493,25 @@ interface AnalyzeOptions {
417
493
  externalRef?: string;
418
494
  /** File name for an upload. Its extension must match the audio format. Taken from the path or File name, or guessed from the bytes. */
419
495
  filename?: string;
496
+ /**
497
+ * The song's lyrics as plain text (up to 20,000 characters), e.g. from Suno. The
498
+ * analysis then uses your exact words on the transcription's timing
499
+ * (`lyrics.source` = "provided_lyrics"); words the singer can't be heard on are
500
+ * left out, never guessed.
501
+ */
502
+ lyrics?: string;
503
+ /**
504
+ * Test mode: free, never charged, no audio needed. The song is done right away and
505
+ * returns the Sugar Rush example analysis with `livemode: false`. `song.done` is
506
+ * still sent to `webhookUrl`. Made for CI and integration tests.
507
+ */
508
+ test?: boolean;
509
+ /**
510
+ * Sent as `Idempotency-Key`. By default a random UUID per call, reused across the
511
+ * client's own retries, so a retried create never makes a second song. Pass your
512
+ * own (e.g. your job id) to make retries across processes safe too (24 h window).
513
+ */
514
+ idempotencyKey?: string;
420
515
  /** Wait until the song is done and return the full document (default true). */
421
516
  wait?: boolean;
422
517
  /** Time between status checks while waiting. Default 5 000. */
@@ -428,6 +523,18 @@ interface WaitOptions {
428
523
  pollIntervalMs?: number;
429
524
  timeoutMs?: number;
430
525
  }
526
+ interface ListSongsOptions {
527
+ /** Page size, 1-100. Default 20. */
528
+ limit?: number;
529
+ /** A song id, usually the previous page's `next_cursor`. Returns the songs after it. */
530
+ startingAfter?: string;
531
+ }
532
+ interface IterSongsOptions {
533
+ /** Songs per request, 1-100. Default 100. */
534
+ pageSize?: number;
535
+ /** Start after this song id. */
536
+ startingAfter?: string;
537
+ }
431
538
  interface GetSongOptions {
432
539
  /** "summary" drops word timings and beat arrays (about 3x smaller). */
433
540
  view?: "full" | "summary";
@@ -450,11 +557,19 @@ declare class Songbrain {
450
557
  readonly timeoutMs: number;
451
558
  readonly maxRetries: number;
452
559
  private readonly fetchImpl;
560
+ /**
561
+ * `{ limit, remaining, reset }` from the `X-RateLimit-*` headers of the last response
562
+ * that had them (null before that). `reset` is seconds until the window has room.
563
+ */
564
+ lastRateLimit: RateLimitInfo | null;
565
+ /** The `Songbrain-Request-Id` of the last response (null before the first one). */
566
+ lastRequestId: string | null;
453
567
  /** @internal Overridable in tests. */
454
568
  _sleep: (ms: number) => Promise<void>;
455
569
  constructor(options?: SongbrainOptions);
456
570
  /**
457
- * Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`.
571
+ * Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`
572
+ * (or neither with `test: true`).
458
573
  * Typically 60-90 s. With `wait: true` (default) resolves to the full document;
459
574
  * with `wait: false` to the 202 body `{ id, status: "processing", eta_sec, billing }`.
460
575
  */
@@ -471,16 +586,31 @@ declare class Songbrain {
471
586
  getSong(songId: string, options?: GetSongOptions): Promise<Song>;
472
587
  /** Only the story and the shot plan: `{ id, status, song, story, shot_plan }`. */
473
588
  shotPlan(songId: string): Promise<SongShotPlan>;
474
- /** Your songs, newest first. `limit` is 1-100 (default 20). */
475
- listSongs(options?: {
476
- limit?: number;
477
- }): Promise<SongList>;
589
+ /** One page of your songs, newest first: `{ object: "list", data, has_more, next_cursor }`. */
590
+ listSongs(options?: ListSongsOptions): Promise<SongList>;
591
+ /**
592
+ * All your songs, newest first, fetching pages as needed.
593
+ *
594
+ * ```ts
595
+ * for await (const item of sb.iterSongs()) console.log(item.id, item.status);
596
+ * ```
597
+ */
598
+ iterSongs(options?: IterSongsOptions): AsyncGenerator<SongListItem, void, undefined>;
478
599
  /** Delete a song's audio and analysis now. 409 `still_processing` while it runs. */
479
600
  deleteSong(songId: string): Promise<DeleteResult>;
480
601
  /** Free songs left this month, credit balance and price per song. */
481
602
  account(): Promise<Account>;
482
603
  /** Prices and limits. No key needed. */
483
604
  pricing(): Promise<Pricing>;
605
+ /**
606
+ * Send a signed `ping` event to `url` right now:
607
+ * `{ delivered, status_code, latency_ms, event_id }`. Checks your receiver before real songs.
608
+ */
609
+ testWebhook(url: string): Promise<WebhookTestResult>;
610
+ /** The last webhook delivery attempts for your account, newest first. */
611
+ webhookDeliveries(options?: {
612
+ limit?: number;
613
+ }): Promise<WebhookDeliveryList>;
484
614
  /** Example analyses of Songbrain's own songs. No key needed. */
485
615
  examples(): Promise<ExampleList>;
486
616
  /** A full example document, in the same format as `getSong`. No key needed. */
@@ -492,9 +622,10 @@ declare class Songbrain {
492
622
  }
493
623
 
494
624
  /**
495
- * Every API error has the shape `{ error: { code, message } }`. The client
496
- * turns it into a `SongbrainError` (or a subclass) with `status`, `code` and
497
- * `message`.
625
+ * Every API error has the shape `{ error: { code, message, request_id } }`.
626
+ * The client turns it into a `SongbrainError` (or a subclass) with `status`,
627
+ * `code`, `message` and `requestId`. The message ends with
628
+ * ` (request_id: req_…)` when there is one; quote it when you contact support.
498
629
  */
499
630
  declare class SongbrainError extends Error {
500
631
  /** HTTP status, or 0 when the error did not come from an HTTP response. */
@@ -503,7 +634,9 @@ declare class SongbrainError extends Error {
503
634
  readonly code: string;
504
635
  /** The parsed response body, when there was one. */
505
636
  readonly body: unknown;
506
- constructor(status: number, code: string, message: string, body?: unknown);
637
+ /** The `Songbrain-Request-Id` of the failed request (`req_…`), or null. */
638
+ readonly requestId: string | null;
639
+ constructor(status: number, code: string, message: string, body?: unknown, requestId?: string | null);
507
640
  }
508
641
  /** 401: the API key is missing, invalid or revoked. */
509
642
  declare class AuthenticationError extends SongbrainError {
@@ -518,7 +651,7 @@ declare class NotFound extends SongbrainError {
518
651
  declare class RateLimited extends SongbrainError {
519
652
  /** Seconds to wait, from the Retry-After header (null if absent). */
520
653
  readonly retryAfter: number | null;
521
- constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null);
654
+ constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null, requestId?: string | null);
522
655
  }
523
656
  /** The song was accepted but its analysis failed. Paid credits are refunded automatically. */
524
657
  declare class AnalysisFailed extends SongbrainError {
@@ -546,13 +679,17 @@ type RawBody = string | Uint8Array | ArrayBuffer;
546
679
  declare function verifyWebhook(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): boolean;
547
680
  /**
548
681
  * Verify the signature and return the parsed event:
549
- * `{ type: "song.done" | "song.failed" | "account.low_balance", created, data }`.
682
+ * `{ id, type: "song.done" | "song.failed" | "account.low_balance" | "ping", created, livemode, data }`.
550
683
  * Throws if the signature is not valid.
684
+ *
685
+ * `id` (`evt_…`, also the `Songbrain-Event-Id` header) stays the same when
686
+ * Songbrain retries an event (up to 10 attempts over about 3 days): store the
687
+ * ids you have handled and skip repeats.
551
688
  */
552
689
  declare function constructWebhookEvent<T = Record<string, unknown>>(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): WebhookEvent<T>;
553
690
  /** Build a signature header value. Useful to test your webhook handler. */
554
691
  declare function signWebhook(rawBody: RawBody, secret: string, timestampSec?: number): string;
555
692
 
556
- declare const VERSION = "0.1.2";
693
+ declare const VERSION = "0.3.0";
557
694
 
558
- export { type Account, type Act, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, type Clip, DEFAULT_BASE_URL, type DeleteResult, type ErrorInfo, type ExampleList, type ExampleSummary, type FileInput, type FullSong, type FullSongSection, type GetSongOptions, type Instrument, InsufficientCredits, type KeyChange, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, NotFound, type Phases, type Pricing, RateLimited, SIGNATURE_HEADER, type Scene, type SceneKind, type ScoreBlock, type Scores, type Section, type SectionName, type ShotPlan, type ShotStyle, type Song, type SongCreated, type SongDNA, type SongInfo, type SongList, type SongListItem, type SongShotPlan, type SongStatus, Songbrain, SongbrainError, type SongbrainOptions, type SoundsLike, type Story, type StoryBeats, type StoryElement, type Timeline, VERSION, type VocalChange, type WaitOptions, WaitTimeout, type WebhookEvent, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };
695
+ export { type Account, type Act, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, type Clip, DEFAULT_BASE_URL, type DeleteResult, type ErrorInfo, type ExampleList, type ExampleSummary, type FileInput, type FullSong, type FullSongSection, type GetSongOptions, type Instrument, InsufficientCredits, type IterSongsOptions, type KeyChange, type ListSongsOptions, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, type LyricsAlignment, NotFound, type Phases, type Pricing, type Provenance, type RateLimitInfo, RateLimited, SIGNATURE_HEADER, type Scene, type SceneKind, type ScoreBlock, type Scores, type Section, type SectionName, type ShotPlan, type ShotStyle, type Song, type SongCreated, type SongDNA, type SongInfo, type SongList, type SongListItem, type SongShotPlan, type SongStatus, Songbrain, SongbrainError, type SongbrainOptions, type SoundsLike, type Story, type StoryBeats, type StoryElement, type Timeline, VERSION, type VocalChange, type WaitOptions, WaitTimeout, type WebhookDelivery, type WebhookDeliveryList, type WebhookEvent, type WebhookTestResult, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };