songbrain 0.1.2 → 0.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.
package/dist/index.d.cts CHANGED
@@ -273,6 +273,8 @@ interface ShotPlan {
273
273
  interface ErrorInfo {
274
274
  code: string;
275
275
  message: string;
276
+ /** "req_…", also in the Songbrain-Request-Id header. */
277
+ request_id?: string;
276
278
  }
277
279
  /**
278
280
  * The song document. While processing, only `id`, `status`, `progress` and
@@ -298,6 +300,8 @@ interface Song {
298
300
  created_at?: string | null;
299
301
  external_ref?: string | null;
300
302
  billing?: Billing;
303
+ /** false for test-mode songs. */
304
+ livemode?: boolean;
301
305
  }
302
306
  /** The 202 body of POST /songs. */
303
307
  interface SongCreated {
@@ -308,6 +312,7 @@ interface SongCreated {
308
312
  billing: Billing;
309
313
  external_ref?: string | null;
310
314
  url: string;
315
+ livemode?: boolean;
311
316
  }
312
317
  /** GET /songs/{id}/shot-plan and GET /examples/{id}/shot-plan. */
313
318
  interface SongShotPlan {
@@ -323,10 +328,14 @@ interface SongListItem {
323
328
  external_ref: string | null;
324
329
  created_at: string | null;
325
330
  billing: Billing;
331
+ livemode?: boolean;
326
332
  }
327
333
  interface SongList {
328
334
  object: "list";
329
335
  data: SongListItem[];
336
+ has_more?: boolean;
337
+ /** Pass as `startingAfter` for the next page. */
338
+ next_cursor?: string | null;
330
339
  }
331
340
  interface DeleteResult {
332
341
  id: string;
@@ -383,11 +392,44 @@ interface Pricing {
383
392
  }
384
393
  /** A webhook event body. */
385
394
  interface WebhookEvent<T = Record<string, unknown>> {
386
- type: "song.done" | "song.failed" | "account.low_balance" | (string & {});
395
+ /** "evt_…". Stays the same across retries of one event: dedupe on it. */
396
+ id: string;
397
+ type: "song.done" | "song.failed" | "account.low_balance" | "ping" | (string & {});
387
398
  /** Unix seconds. */
388
399
  created: number;
400
+ livemode: boolean;
389
401
  data: T;
390
402
  }
403
+ /** From the X-RateLimit-* headers. See `Songbrain.lastRateLimit`. */
404
+ interface RateLimitInfo {
405
+ /** Requests per minute. */
406
+ limit: number;
407
+ remaining: number | null;
408
+ /** Seconds until the window has room again. */
409
+ reset: number | null;
410
+ }
411
+ /** POST /webhooks/test. */
412
+ interface WebhookTestResult {
413
+ delivered: boolean;
414
+ status_code: number | null;
415
+ latency_ms: number;
416
+ event_id: string;
417
+ }
418
+ interface WebhookDelivery {
419
+ event_id: string;
420
+ type: WebhookEvent["type"];
421
+ song_id: string | null;
422
+ url: string;
423
+ attempt: number;
424
+ status_code: number | null;
425
+ delivered: boolean;
426
+ latency_ms: number | null;
427
+ created_at: string;
428
+ }
429
+ interface WebhookDeliveryList {
430
+ object: "list";
431
+ data: WebhookDelivery[];
432
+ }
391
433
 
392
434
  declare const DEFAULT_BASE_URL = "https://api.songbrain.ai/v1";
393
435
  /** A Blob/File, a Buffer or bytes, or a local file path (Node). */
@@ -399,7 +441,10 @@ interface SongbrainOptions {
399
441
  baseUrl?: string;
400
442
  /** Timeout for each HTTP request in ms (uploads included). Default 60 000. */
401
443
  timeoutMs?: number;
402
- /** How often to retry 429 and 5xx responses, with backoff. Default 3. */
444
+ /**
445
+ * How often to retry network errors, 429 and 5xx responses, with backoff. Default 3.
446
+ * Song creation is retried too, because every create sends an `Idempotency-Key`.
447
+ */
403
448
  maxRetries?: number;
404
449
  /** A custom fetch implementation. Defaults to the global fetch. */
405
450
  fetch?: typeof fetch;
@@ -417,6 +462,18 @@ interface AnalyzeOptions {
417
462
  externalRef?: string;
418
463
  /** 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
464
  filename?: string;
465
+ /**
466
+ * Test mode: free, never charged, no audio needed. The song is done right away and
467
+ * returns the Sugar Rush example analysis with `livemode: false`. `song.done` is
468
+ * still sent to `webhookUrl`. Made for CI and integration tests.
469
+ */
470
+ test?: boolean;
471
+ /**
472
+ * Sent as `Idempotency-Key`. By default a random UUID per call, reused across the
473
+ * client's own retries, so a retried create never makes a second song. Pass your
474
+ * own (e.g. your job id) to make retries across processes safe too (24 h window).
475
+ */
476
+ idempotencyKey?: string;
420
477
  /** Wait until the song is done and return the full document (default true). */
421
478
  wait?: boolean;
422
479
  /** Time between status checks while waiting. Default 5 000. */
@@ -428,6 +485,18 @@ interface WaitOptions {
428
485
  pollIntervalMs?: number;
429
486
  timeoutMs?: number;
430
487
  }
488
+ interface ListSongsOptions {
489
+ /** Page size, 1-100. Default 20. */
490
+ limit?: number;
491
+ /** A song id, usually the previous page's `next_cursor`. Returns the songs after it. */
492
+ startingAfter?: string;
493
+ }
494
+ interface IterSongsOptions {
495
+ /** Songs per request, 1-100. Default 100. */
496
+ pageSize?: number;
497
+ /** Start after this song id. */
498
+ startingAfter?: string;
499
+ }
431
500
  interface GetSongOptions {
432
501
  /** "summary" drops word timings and beat arrays (about 3x smaller). */
433
502
  view?: "full" | "summary";
@@ -450,11 +519,19 @@ declare class Songbrain {
450
519
  readonly timeoutMs: number;
451
520
  readonly maxRetries: number;
452
521
  private readonly fetchImpl;
522
+ /**
523
+ * `{ limit, remaining, reset }` from the `X-RateLimit-*` headers of the last response
524
+ * that had them (null before that). `reset` is seconds until the window has room.
525
+ */
526
+ lastRateLimit: RateLimitInfo | null;
527
+ /** The `Songbrain-Request-Id` of the last response (null before the first one). */
528
+ lastRequestId: string | null;
453
529
  /** @internal Overridable in tests. */
454
530
  _sleep: (ms: number) => Promise<void>;
455
531
  constructor(options?: SongbrainOptions);
456
532
  /**
457
- * Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`.
533
+ * Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`
534
+ * (or neither with `test: true`).
458
535
  * Typically 60-90 s. With `wait: true` (default) resolves to the full document;
459
536
  * with `wait: false` to the 202 body `{ id, status: "processing", eta_sec, billing }`.
460
537
  */
@@ -471,16 +548,31 @@ declare class Songbrain {
471
548
  getSong(songId: string, options?: GetSongOptions): Promise<Song>;
472
549
  /** Only the story and the shot plan: `{ id, status, song, story, shot_plan }`. */
473
550
  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>;
551
+ /** One page of your songs, newest first: `{ object: "list", data, has_more, next_cursor }`. */
552
+ listSongs(options?: ListSongsOptions): Promise<SongList>;
553
+ /**
554
+ * All your songs, newest first, fetching pages as needed.
555
+ *
556
+ * ```ts
557
+ * for await (const item of sb.iterSongs()) console.log(item.id, item.status);
558
+ * ```
559
+ */
560
+ iterSongs(options?: IterSongsOptions): AsyncGenerator<SongListItem, void, undefined>;
478
561
  /** Delete a song's audio and analysis now. 409 `still_processing` while it runs. */
479
562
  deleteSong(songId: string): Promise<DeleteResult>;
480
563
  /** Free songs left this month, credit balance and price per song. */
481
564
  account(): Promise<Account>;
482
565
  /** Prices and limits. No key needed. */
483
566
  pricing(): Promise<Pricing>;
567
+ /**
568
+ * Send a signed `ping` event to `url` right now:
569
+ * `{ delivered, status_code, latency_ms, event_id }`. Checks your receiver before real songs.
570
+ */
571
+ testWebhook(url: string): Promise<WebhookTestResult>;
572
+ /** The last webhook delivery attempts for your account, newest first. */
573
+ webhookDeliveries(options?: {
574
+ limit?: number;
575
+ }): Promise<WebhookDeliveryList>;
484
576
  /** Example analyses of Songbrain's own songs. No key needed. */
485
577
  examples(): Promise<ExampleList>;
486
578
  /** A full example document, in the same format as `getSong`. No key needed. */
@@ -492,9 +584,10 @@ declare class Songbrain {
492
584
  }
493
585
 
494
586
  /**
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`.
587
+ * Every API error has the shape `{ error: { code, message, request_id } }`.
588
+ * The client turns it into a `SongbrainError` (or a subclass) with `status`,
589
+ * `code`, `message` and `requestId`. The message ends with
590
+ * ` (request_id: req_…)` when there is one; quote it when you contact support.
498
591
  */
499
592
  declare class SongbrainError extends Error {
500
593
  /** HTTP status, or 0 when the error did not come from an HTTP response. */
@@ -503,7 +596,9 @@ declare class SongbrainError extends Error {
503
596
  readonly code: string;
504
597
  /** The parsed response body, when there was one. */
505
598
  readonly body: unknown;
506
- constructor(status: number, code: string, message: string, body?: unknown);
599
+ /** The `Songbrain-Request-Id` of the failed request (`req_…`), or null. */
600
+ readonly requestId: string | null;
601
+ constructor(status: number, code: string, message: string, body?: unknown, requestId?: string | null);
507
602
  }
508
603
  /** 401: the API key is missing, invalid or revoked. */
509
604
  declare class AuthenticationError extends SongbrainError {
@@ -518,7 +613,7 @@ declare class NotFound extends SongbrainError {
518
613
  declare class RateLimited extends SongbrainError {
519
614
  /** Seconds to wait, from the Retry-After header (null if absent). */
520
615
  readonly retryAfter: number | null;
521
- constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null);
616
+ constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null, requestId?: string | null);
522
617
  }
523
618
  /** The song was accepted but its analysis failed. Paid credits are refunded automatically. */
524
619
  declare class AnalysisFailed extends SongbrainError {
@@ -546,13 +641,17 @@ type RawBody = string | Uint8Array | ArrayBuffer;
546
641
  declare function verifyWebhook(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): boolean;
547
642
  /**
548
643
  * Verify the signature and return the parsed event:
549
- * `{ type: "song.done" | "song.failed" | "account.low_balance", created, data }`.
644
+ * `{ id, type: "song.done" | "song.failed" | "account.low_balance" | "ping", created, livemode, data }`.
550
645
  * Throws if the signature is not valid.
646
+ *
647
+ * `id` (`evt_…`, also the `Songbrain-Event-Id` header) stays the same when
648
+ * Songbrain retries an event (up to 10 attempts over about 3 days): store the
649
+ * ids you have handled and skip repeats.
551
650
  */
552
651
  declare function constructWebhookEvent<T = Record<string, unknown>>(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): WebhookEvent<T>;
553
652
  /** Build a signature header value. Useful to test your webhook handler. */
554
653
  declare function signWebhook(rawBody: RawBody, secret: string, timestampSec?: number): string;
555
654
 
556
- declare const VERSION = "0.1.2";
655
+ declare const VERSION = "0.2.0";
557
656
 
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 };
657
+ 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, NotFound, type Phases, type Pricing, 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
@@ -273,6 +273,8 @@ interface ShotPlan {
273
273
  interface ErrorInfo {
274
274
  code: string;
275
275
  message: string;
276
+ /** "req_…", also in the Songbrain-Request-Id header. */
277
+ request_id?: string;
276
278
  }
277
279
  /**
278
280
  * The song document. While processing, only `id`, `status`, `progress` and
@@ -298,6 +300,8 @@ interface Song {
298
300
  created_at?: string | null;
299
301
  external_ref?: string | null;
300
302
  billing?: Billing;
303
+ /** false for test-mode songs. */
304
+ livemode?: boolean;
301
305
  }
302
306
  /** The 202 body of POST /songs. */
303
307
  interface SongCreated {
@@ -308,6 +312,7 @@ interface SongCreated {
308
312
  billing: Billing;
309
313
  external_ref?: string | null;
310
314
  url: string;
315
+ livemode?: boolean;
311
316
  }
312
317
  /** GET /songs/{id}/shot-plan and GET /examples/{id}/shot-plan. */
313
318
  interface SongShotPlan {
@@ -323,10 +328,14 @@ interface SongListItem {
323
328
  external_ref: string | null;
324
329
  created_at: string | null;
325
330
  billing: Billing;
331
+ livemode?: boolean;
326
332
  }
327
333
  interface SongList {
328
334
  object: "list";
329
335
  data: SongListItem[];
336
+ has_more?: boolean;
337
+ /** Pass as `startingAfter` for the next page. */
338
+ next_cursor?: string | null;
330
339
  }
331
340
  interface DeleteResult {
332
341
  id: string;
@@ -383,11 +392,44 @@ interface Pricing {
383
392
  }
384
393
  /** A webhook event body. */
385
394
  interface WebhookEvent<T = Record<string, unknown>> {
386
- type: "song.done" | "song.failed" | "account.low_balance" | (string & {});
395
+ /** "evt_…". Stays the same across retries of one event: dedupe on it. */
396
+ id: string;
397
+ type: "song.done" | "song.failed" | "account.low_balance" | "ping" | (string & {});
387
398
  /** Unix seconds. */
388
399
  created: number;
400
+ livemode: boolean;
389
401
  data: T;
390
402
  }
403
+ /** From the X-RateLimit-* headers. See `Songbrain.lastRateLimit`. */
404
+ interface RateLimitInfo {
405
+ /** Requests per minute. */
406
+ limit: number;
407
+ remaining: number | null;
408
+ /** Seconds until the window has room again. */
409
+ reset: number | null;
410
+ }
411
+ /** POST /webhooks/test. */
412
+ interface WebhookTestResult {
413
+ delivered: boolean;
414
+ status_code: number | null;
415
+ latency_ms: number;
416
+ event_id: string;
417
+ }
418
+ interface WebhookDelivery {
419
+ event_id: string;
420
+ type: WebhookEvent["type"];
421
+ song_id: string | null;
422
+ url: string;
423
+ attempt: number;
424
+ status_code: number | null;
425
+ delivered: boolean;
426
+ latency_ms: number | null;
427
+ created_at: string;
428
+ }
429
+ interface WebhookDeliveryList {
430
+ object: "list";
431
+ data: WebhookDelivery[];
432
+ }
391
433
 
392
434
  declare const DEFAULT_BASE_URL = "https://api.songbrain.ai/v1";
393
435
  /** A Blob/File, a Buffer or bytes, or a local file path (Node). */
@@ -399,7 +441,10 @@ interface SongbrainOptions {
399
441
  baseUrl?: string;
400
442
  /** Timeout for each HTTP request in ms (uploads included). Default 60 000. */
401
443
  timeoutMs?: number;
402
- /** How often to retry 429 and 5xx responses, with backoff. Default 3. */
444
+ /**
445
+ * How often to retry network errors, 429 and 5xx responses, with backoff. Default 3.
446
+ * Song creation is retried too, because every create sends an `Idempotency-Key`.
447
+ */
403
448
  maxRetries?: number;
404
449
  /** A custom fetch implementation. Defaults to the global fetch. */
405
450
  fetch?: typeof fetch;
@@ -417,6 +462,18 @@ interface AnalyzeOptions {
417
462
  externalRef?: string;
418
463
  /** 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
464
  filename?: string;
465
+ /**
466
+ * Test mode: free, never charged, no audio needed. The song is done right away and
467
+ * returns the Sugar Rush example analysis with `livemode: false`. `song.done` is
468
+ * still sent to `webhookUrl`. Made for CI and integration tests.
469
+ */
470
+ test?: boolean;
471
+ /**
472
+ * Sent as `Idempotency-Key`. By default a random UUID per call, reused across the
473
+ * client's own retries, so a retried create never makes a second song. Pass your
474
+ * own (e.g. your job id) to make retries across processes safe too (24 h window).
475
+ */
476
+ idempotencyKey?: string;
420
477
  /** Wait until the song is done and return the full document (default true). */
421
478
  wait?: boolean;
422
479
  /** Time between status checks while waiting. Default 5 000. */
@@ -428,6 +485,18 @@ interface WaitOptions {
428
485
  pollIntervalMs?: number;
429
486
  timeoutMs?: number;
430
487
  }
488
+ interface ListSongsOptions {
489
+ /** Page size, 1-100. Default 20. */
490
+ limit?: number;
491
+ /** A song id, usually the previous page's `next_cursor`. Returns the songs after it. */
492
+ startingAfter?: string;
493
+ }
494
+ interface IterSongsOptions {
495
+ /** Songs per request, 1-100. Default 100. */
496
+ pageSize?: number;
497
+ /** Start after this song id. */
498
+ startingAfter?: string;
499
+ }
431
500
  interface GetSongOptions {
432
501
  /** "summary" drops word timings and beat arrays (about 3x smaller). */
433
502
  view?: "full" | "summary";
@@ -450,11 +519,19 @@ declare class Songbrain {
450
519
  readonly timeoutMs: number;
451
520
  readonly maxRetries: number;
452
521
  private readonly fetchImpl;
522
+ /**
523
+ * `{ limit, remaining, reset }` from the `X-RateLimit-*` headers of the last response
524
+ * that had them (null before that). `reset` is seconds until the window has room.
525
+ */
526
+ lastRateLimit: RateLimitInfo | null;
527
+ /** The `Songbrain-Request-Id` of the last response (null before the first one). */
528
+ lastRequestId: string | null;
453
529
  /** @internal Overridable in tests. */
454
530
  _sleep: (ms: number) => Promise<void>;
455
531
  constructor(options?: SongbrainOptions);
456
532
  /**
457
- * Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`.
533
+ * Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`
534
+ * (or neither with `test: true`).
458
535
  * Typically 60-90 s. With `wait: true` (default) resolves to the full document;
459
536
  * with `wait: false` to the 202 body `{ id, status: "processing", eta_sec, billing }`.
460
537
  */
@@ -471,16 +548,31 @@ declare class Songbrain {
471
548
  getSong(songId: string, options?: GetSongOptions): Promise<Song>;
472
549
  /** Only the story and the shot plan: `{ id, status, song, story, shot_plan }`. */
473
550
  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>;
551
+ /** One page of your songs, newest first: `{ object: "list", data, has_more, next_cursor }`. */
552
+ listSongs(options?: ListSongsOptions): Promise<SongList>;
553
+ /**
554
+ * All your songs, newest first, fetching pages as needed.
555
+ *
556
+ * ```ts
557
+ * for await (const item of sb.iterSongs()) console.log(item.id, item.status);
558
+ * ```
559
+ */
560
+ iterSongs(options?: IterSongsOptions): AsyncGenerator<SongListItem, void, undefined>;
478
561
  /** Delete a song's audio and analysis now. 409 `still_processing` while it runs. */
479
562
  deleteSong(songId: string): Promise<DeleteResult>;
480
563
  /** Free songs left this month, credit balance and price per song. */
481
564
  account(): Promise<Account>;
482
565
  /** Prices and limits. No key needed. */
483
566
  pricing(): Promise<Pricing>;
567
+ /**
568
+ * Send a signed `ping` event to `url` right now:
569
+ * `{ delivered, status_code, latency_ms, event_id }`. Checks your receiver before real songs.
570
+ */
571
+ testWebhook(url: string): Promise<WebhookTestResult>;
572
+ /** The last webhook delivery attempts for your account, newest first. */
573
+ webhookDeliveries(options?: {
574
+ limit?: number;
575
+ }): Promise<WebhookDeliveryList>;
484
576
  /** Example analyses of Songbrain's own songs. No key needed. */
485
577
  examples(): Promise<ExampleList>;
486
578
  /** A full example document, in the same format as `getSong`. No key needed. */
@@ -492,9 +584,10 @@ declare class Songbrain {
492
584
  }
493
585
 
494
586
  /**
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`.
587
+ * Every API error has the shape `{ error: { code, message, request_id } }`.
588
+ * The client turns it into a `SongbrainError` (or a subclass) with `status`,
589
+ * `code`, `message` and `requestId`. The message ends with
590
+ * ` (request_id: req_…)` when there is one; quote it when you contact support.
498
591
  */
499
592
  declare class SongbrainError extends Error {
500
593
  /** HTTP status, or 0 when the error did not come from an HTTP response. */
@@ -503,7 +596,9 @@ declare class SongbrainError extends Error {
503
596
  readonly code: string;
504
597
  /** The parsed response body, when there was one. */
505
598
  readonly body: unknown;
506
- constructor(status: number, code: string, message: string, body?: unknown);
599
+ /** The `Songbrain-Request-Id` of the failed request (`req_…`), or null. */
600
+ readonly requestId: string | null;
601
+ constructor(status: number, code: string, message: string, body?: unknown, requestId?: string | null);
507
602
  }
508
603
  /** 401: the API key is missing, invalid or revoked. */
509
604
  declare class AuthenticationError extends SongbrainError {
@@ -518,7 +613,7 @@ declare class NotFound extends SongbrainError {
518
613
  declare class RateLimited extends SongbrainError {
519
614
  /** Seconds to wait, from the Retry-After header (null if absent). */
520
615
  readonly retryAfter: number | null;
521
- constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null);
616
+ constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null, requestId?: string | null);
522
617
  }
523
618
  /** The song was accepted but its analysis failed. Paid credits are refunded automatically. */
524
619
  declare class AnalysisFailed extends SongbrainError {
@@ -546,13 +641,17 @@ type RawBody = string | Uint8Array | ArrayBuffer;
546
641
  declare function verifyWebhook(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): boolean;
547
642
  /**
548
643
  * Verify the signature and return the parsed event:
549
- * `{ type: "song.done" | "song.failed" | "account.low_balance", created, data }`.
644
+ * `{ id, type: "song.done" | "song.failed" | "account.low_balance" | "ping", created, livemode, data }`.
550
645
  * Throws if the signature is not valid.
646
+ *
647
+ * `id` (`evt_…`, also the `Songbrain-Event-Id` header) stays the same when
648
+ * Songbrain retries an event (up to 10 attempts over about 3 days): store the
649
+ * ids you have handled and skip repeats.
551
650
  */
552
651
  declare function constructWebhookEvent<T = Record<string, unknown>>(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): WebhookEvent<T>;
553
652
  /** Build a signature header value. Useful to test your webhook handler. */
554
653
  declare function signWebhook(rawBody: RawBody, secret: string, timestampSec?: number): string;
555
654
 
556
- declare const VERSION = "0.1.2";
655
+ declare const VERSION = "0.2.0";
557
656
 
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 };
657
+ 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, NotFound, type Phases, type Pricing, 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 };