@spekoai/sdk 0.4.3 → 0.5.2

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +88 -0
  3. package/dist/index.d.ts +3 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -0
  6. package/dist/lib/client.d.ts +13 -1
  7. package/dist/lib/client.d.ts.map +1 -1
  8. package/dist/lib/client.js +22 -2
  9. package/dist/lib/http.d.ts +12 -4
  10. package/dist/lib/http.d.ts.map +1 -1
  11. package/dist/lib/http.js +31 -14
  12. package/dist/lib/resources/agents.d.ts +12 -2
  13. package/dist/lib/resources/agents.d.ts.map +1 -1
  14. package/dist/lib/resources/agents.js +12 -4
  15. package/dist/lib/resources/calls.d.ts +19 -1
  16. package/dist/lib/resources/calls.d.ts.map +1 -1
  17. package/dist/lib/resources/calls.js +22 -0
  18. package/dist/lib/resources/knowledge-bases.d.ts +1 -1
  19. package/dist/lib/resources/knowledge-bases.js +1 -1
  20. package/dist/lib/resources/phone-numbers.d.ts +2 -1
  21. package/dist/lib/resources/phone-numbers.d.ts.map +1 -1
  22. package/dist/lib/resources/phone-numbers.js +2 -1
  23. package/dist/lib/resources/realtime.d.ts +3 -5
  24. package/dist/lib/resources/realtime.d.ts.map +1 -1
  25. package/dist/lib/resources/realtime.js +829 -91
  26. package/dist/lib/resources/sessions.d.ts +53 -0
  27. package/dist/lib/resources/sessions.d.ts.map +1 -0
  28. package/dist/lib/resources/sessions.js +166 -0
  29. package/dist/lib/resources/sms.d.ts +80 -0
  30. package/dist/lib/resources/sms.d.ts.map +1 -0
  31. package/dist/lib/resources/sms.js +152 -0
  32. package/dist/lib/resources/synthesize.d.ts.map +1 -1
  33. package/dist/lib/resources/synthesize.js +12 -6
  34. package/dist/lib/resources/transcribe.d.ts.map +1 -1
  35. package/dist/lib/resources/transcribe.js +9 -2
  36. package/dist/lib/resources/voice.d.ts +283 -1
  37. package/dist/lib/resources/voice.d.ts.map +1 -1
  38. package/dist/lib/resources/voice.js +345 -0
  39. package/dist/lib/resources/webhooks.d.ts +25 -0
  40. package/dist/lib/resources/webhooks.d.ts.map +1 -0
  41. package/dist/lib/resources/webhooks.js +46 -0
  42. package/dist/lib/types/index.d.ts +998 -9
  43. package/dist/lib/types/index.d.ts.map +1 -1
  44. package/dist/lib/voice-contract.d.ts +280 -0
  45. package/dist/lib/voice-contract.d.ts.map +1 -0
  46. package/dist/lib/voice-contract.js +115 -0
  47. package/package.json +2 -1
  48. package/src/index.ts +212 -0
  49. package/src/lib/client.ts +169 -0
  50. package/src/lib/errors.ts +28 -0
  51. package/src/lib/http.ts +442 -0
  52. package/src/lib/resources/agents.ts +211 -0
  53. package/src/lib/resources/callbacks.ts +40 -0
  54. package/src/lib/resources/calls.ts +113 -0
  55. package/src/lib/resources/complete.ts +63 -0
  56. package/src/lib/resources/credits.ts +41 -0
  57. package/src/lib/resources/knowledge-bases.ts +199 -0
  58. package/src/lib/resources/phone-numbers.ts +109 -0
  59. package/src/lib/resources/realtime-globals.d.ts +31 -0
  60. package/src/lib/resources/realtime.spec.ts +565 -0
  61. package/src/lib/resources/realtime.ts +1169 -0
  62. package/src/lib/resources/sessions.ts +191 -0
  63. package/src/lib/resources/sms.ts +214 -0
  64. package/src/lib/resources/synthesize.ts +101 -0
  65. package/src/lib/resources/transcribe.ts +91 -0
  66. package/src/lib/resources/usage.ts +24 -0
  67. package/src/lib/resources/voice.ts +426 -0
  68. package/src/lib/resources/voices.ts +32 -0
  69. package/src/lib/resources/webhooks.ts +67 -0
  70. package/src/lib/types/index.ts +2409 -0
  71. package/src/lib/voice-contract.ts +358 -0
@@ -1,7 +1,10 @@
1
+ import type { CallDirection, CallJoinCredentials, CallResource, CallStatus } from '../voice-contract.js';
1
2
  /** Options for creating a Speko client. */
2
3
  export interface SpekoClientOptions {
3
4
  /** API key for authentication. */
4
5
  apiKey: string;
6
+ /** Org-defined broker identity used by human-calling methods. */
7
+ brokerId?: string;
5
8
  /** Base URL of the Speko API. Defaults to https://api.speko.dev */
6
9
  baseUrl?: string;
7
10
  /** Alias for {@link SpekoClientOptions.baseUrl}. If both are set, `baseUrl` wins. */
@@ -89,6 +92,12 @@ export interface PipelineConstraints {
89
92
  };
90
93
  }
91
94
  export interface TranscribeOptions extends RoutingIntent {
95
+ /**
96
+ * Optional voice/session identifier forwarded as `x-session-id` for usage
97
+ * attribution. The value is carried out-of-band so request bodies and STT
98
+ * provider options stay provider-shaped.
99
+ */
100
+ sessionId?: string;
92
101
  /** MIME type of the audio body. Defaults to "audio/wav". */
93
102
  contentType?: string;
94
103
  constraints?: PipelineConstraints;
@@ -99,6 +108,10 @@ export interface TranscribeOptions extends RoutingIntent {
99
108
  * ElevenLabs Scribe → `biased_keywords`. Casing matters for proper nouns.
100
109
  */
101
110
  keywords?: readonly string[];
111
+ /** Provider-facing STT overrides. Routing continues to use the inherited language. */
112
+ sttOptions?: {
113
+ language?: string;
114
+ };
102
115
  }
103
116
  export interface TranscribeResult {
104
117
  text: string;
@@ -127,6 +140,12 @@ export type TranscribeStreamEvent = {
127
140
  code: string;
128
141
  };
129
142
  export interface SynthesizeOptions extends RoutingIntent {
143
+ /**
144
+ * Optional voice/session identifier forwarded as `x-session-id` for usage
145
+ * attribution. The value is carried out-of-band so request bodies and TTS
146
+ * provider options stay provider-shaped.
147
+ */
148
+ sessionId?: string;
130
149
  /** Optional voice override. Otherwise the SDK uses each provider's default. */
131
150
  voice?: string;
132
151
  /**
@@ -139,6 +158,20 @@ export interface SynthesizeOptions extends RoutingIntent {
139
158
  */
140
159
  model?: string;
141
160
  speed?: number;
161
+ /**
162
+ * Free-text speaking-style instruction (tone, pace, emotion) forwarded to the
163
+ * TTS model. Only instruction-capable models honor it (OpenAI
164
+ * `gpt-4o-mini-tts`, Hume Octave, `qwen3-tts-instruct-flash`); the router
165
+ * drops it for any other resolved model, so it's safe to always pass.
166
+ */
167
+ instructions?: string;
168
+ /**
169
+ * Normalize the text into spoken form before TTS — strip markdown/URLs, spell
170
+ * out numbers/currency/abbreviations. A deterministic safety net beneath the
171
+ * voice directive. The voice pipeline sets this; direct TTS callers default
172
+ * off and get literal text.
173
+ */
174
+ spokenForm?: boolean;
142
175
  constraints?: PipelineConstraints;
143
176
  }
144
177
  export interface SynthesizeResult {
@@ -224,6 +257,13 @@ export interface ChatMessage {
224
257
  * org-installed Speko app action such as Google Calendar or Slack.
225
258
  */
226
259
  export type ChatToolExecutionMode = 'inline' | 'webhook' | 'builtin' | 'integration';
260
+ /**
261
+ * Spoken lead-in behavior before a server-executed tool runs. `auto` lets the
262
+ * gateway decide from the tool's recent execution durations; `always` forces a
263
+ * spoken lead-in (the gateway injects one when the model didn't produce any);
264
+ * `never` runs the tool silently.
265
+ */
266
+ export type ChatToolPreToolSpeech = 'auto' | 'always' | 'never';
227
267
  /**
228
268
  * Source-of-execution config. Required when `executionMode` is
229
269
  * `webhook`, `builtin`, or `integration`. Mirrors the SpekoTool `source` shape inside
@@ -237,6 +277,15 @@ export type ChatToolSource = {
237
277
  /** Pointer into Speko's secrets store. Created via `POST /v1/agents/:id/tools` (which encrypts and stores the raw secret). */
238
278
  secretRef: string;
239
279
  headers?: Record<string, string>;
280
+ /**
281
+ * Outbound auth headers whose values are secret-referenced (resolved and
282
+ * injected by Speko at call time). The raw credential never leaves the
283
+ * server — only the `secretRef` pointer is exposed.
284
+ */
285
+ authHeaders?: Array<{
286
+ name: string;
287
+ secretRef: string;
288
+ }>;
240
289
  timeoutMs?: number;
241
290
  /** `async` returns `asyncAck` immediately while Speko dispatches the webhook in the background. */
242
291
  responseMode?: 'sync' | 'async';
@@ -269,6 +318,8 @@ export interface ChatTool {
269
318
  parameters: Record<string, unknown>;
270
319
  executionMode?: ChatToolExecutionMode;
271
320
  source?: ChatToolSource;
321
+ /** Spoken lead-in behavior before this tool executes. Defaults to `auto` for registered tools. */
322
+ preToolSpeech?: ChatToolPreToolSpeech;
272
323
  }
273
324
  /** Mirrors LiveKit's `ToolChoice` for parity with the agents framework. */
274
325
  export type ChatToolChoice = 'auto' | 'none' | 'required' | {
@@ -342,13 +393,15 @@ export type CompleteStreamEvent = {
342
393
  error: string;
343
394
  code: string;
344
395
  };
345
- export type RealtimeProvider = 'openai' | 'google' | 'xai' | 'inworld' | 'alibaba';
396
+ export type RealtimeProvider = 'openai' | 'google' | 'xai';
346
397
  export interface RealtimeToolSpec {
347
398
  name: string;
348
399
  description: string;
349
400
  parameters: Record<string, unknown>;
350
401
  }
351
402
  export interface RealtimeConnectParams {
403
+ /** Persisted agent whose workspace webhook routes should receive lifecycle events. */
404
+ agentId?: string;
352
405
  provider: RealtimeProvider;
353
406
  model: string;
354
407
  voice?: string;
@@ -357,9 +410,13 @@ export interface RealtimeConnectParams {
357
410
  inputSampleRate?: 16000 | 24000;
358
411
  outputSampleRate?: 16000 | 24000;
359
412
  tools?: RealtimeToolSpec[];
413
+ /** Exact-match attributes used only for workspace webhook routing. Requires agentId. */
414
+ webhookTags?: Record<string, string>;
360
415
  metadata?: Record<string, unknown>;
361
416
  /** Max session duration in seconds. Server-capped at 1800 (30 min). */
362
417
  ttlSeconds?: number;
418
+ /** Reuse when retrying an ambiguous bootstrap timeout. Generated when omitted. */
419
+ idempotencyKey?: string;
363
420
  }
364
421
  /**
365
422
  * Event shape emitted by a `RealtimeSessionHandle`. Binary audio comes in
@@ -439,6 +496,52 @@ export interface VoiceDialParams {
439
496
  systemPrompt?: string;
440
497
  /** Optional first utterance. `null` is not accepted by phone dial; omit to use the agent default. */
441
498
  firstMessage?: string;
499
+ /**
500
+ * Call-time values for template variables in `systemPrompt` / `firstMessage`.
501
+ * Sending this key (even `{}`) — or dialing with an agent that declares
502
+ * variables in its registry — compiles both strings as Liquid templates at
503
+ * call-create time: `{{name}}` interpolation, `{% if %}` / `{% elsif %}`
504
+ * branching, `| default:` filters, and platform-provided `system.*` values
505
+ * (`system.now`, `system.caller_number`, `system.call_id`, …).
506
+ *
507
+ * Resolution per variable: this map → the agent registry's default → inline
508
+ * `| default:` → the request fails with 400 `MISSING_TEMPLATE_VARIABLES`
509
+ * listing every unresolved name. Keys under `system.` are rejected. Omit
510
+ * this field entirely to send both strings verbatim (no compilation).
511
+ *
512
+ * @example
513
+ * ```ts
514
+ * await speko.voice.dial({
515
+ * to: '+12015551234',
516
+ * agentId: 'ag_123',
517
+ * systemPrompt:
518
+ * 'You are {{agent_name | default: "Ava"}} calling {{customer}}. ' +
519
+ * '{% if plan == "premium" %}Offer the priority upgrade.{% endif %} ' +
520
+ * 'The current time is {{system.now}}.',
521
+ * variables: { customer: 'Mr. Lee', plan: 'premium' },
522
+ * });
523
+ * ```
524
+ */
525
+ variables?: Record<string, string>;
526
+ /**
527
+ * Per-call values for TOOLS ONLY — e.g. a short-lived access token scoped to
528
+ * the person being called, or a per-tenant API base URL. Unlike `variables`
529
+ * these never enter the system prompt, the transcript, or the model's
530
+ * context; they are stored encrypted and released only to tool execution.
531
+ * Custom-code tools read `session.secrets.<name>`; webhook tools may
532
+ * reference `{{name}}` in their `url` and `headers`. Names must be
533
+ * identifiers (`[A-Za-z_][A-Za-z0-9_]*`); up to 32 entries, 6 chars–4 KB each.
534
+ *
535
+ * @example
536
+ * ```ts
537
+ * await speko.voice.dial({
538
+ * to: '+12015551234',
539
+ * agentId: 'ag_123',
540
+ * toolSecrets: { base_url: 'https://acme.example.com', access_token: token },
541
+ * });
542
+ * ```
543
+ */
544
+ toolSecrets?: Record<string, string>;
442
545
  llm?: {
443
546
  temperature?: number;
444
547
  maxTokens?: number;
@@ -449,6 +552,60 @@ export interface VoiceDialParams {
449
552
  };
450
553
  sttOptions?: {
451
554
  keywords?: string[];
555
+ prompt?: string;
556
+ language?: string;
557
+ };
558
+ /** Server-side wall-clock cap in seconds. Values are clamped server-side to 30s-4h. */
559
+ maxDurationSeconds?: number;
560
+ /**
561
+ * Optional per-call turn-taking overrides. `greetFirst` defaults ON for
562
+ * outbound (worker-side, 2026-07-03): the greeting plays immediately while
563
+ * AMD classifies in the background. Pass false to hold the greeting for the
564
+ * AMD verdict.
565
+ */
566
+ turnHandling?: {
567
+ /** Local VAD for cascaded calls. Omit to use Silero. */
568
+ vad?: {
569
+ provider: 'silero' | 'ai-coustics';
570
+ };
571
+ /**
572
+ * Caller-leg input enhancement (ai-coustics). Omit for the platform default;
573
+ * `enabled: false` turns it off; `model` is plain Quail (default) or Quail Voice
574
+ * Focus (primary-speaker isolation, explicit opt-in).
575
+ */
576
+ noiseCancellation?: {
577
+ enabled: boolean;
578
+ model?: 'quail' | 'quail-voice-focus';
579
+ };
580
+ profile?: 'conversational' | 'ivr' | 'ivr_patient';
581
+ endpointing?: {
582
+ minDelay?: number;
583
+ maxDelay?: number;
584
+ };
585
+ interruption?: {
586
+ mode?: 'adaptive' | 'vad';
587
+ minDuration?: number;
588
+ minWords?: number;
589
+ };
590
+ turnDetection?: boolean | 'stt';
591
+ contextThreshold?: boolean;
592
+ greetFirst?: boolean;
593
+ /**
594
+ * Replaces the built-in prompt the answering-machine detector's classifier
595
+ * sees when deciding whether a human, an IVR menu or voicemail answered.
596
+ * Outbound only; max 2,000 characters.
597
+ */
598
+ amdPrompt?: string;
599
+ /**
600
+ * What happens when native detection identifies recordable voicemail. `hangup` ends the call at the verdict; `leave_message` waits
601
+ * for the greeting to finish, speaks `voicemailMessage` once, then hangs
602
+ * up; `agent_decides` (default) hands the verdict to the LLM and lets the
603
+ * prompt's own voicemail rules act. Unavailable mailboxes always end without
604
+ * a message. Does not apply to menus/screeners or enable disabled/carrier AMD.
605
+ */
606
+ onMachine?: 'hangup' | 'leave_message' | 'agent_decides';
607
+ /** Spoken once into the mailbox under `onMachine: 'leave_message'`. Renders the same `{{variables}}` as `firstMessage`. Max 2,000 characters. */
608
+ voicemailMessage?: string;
452
609
  };
453
610
  /** Optional per-call SIP routing hints. Carrier AMD requires trunk/provider support. */
454
611
  telephony?: {
@@ -458,8 +615,17 @@ export interface VoiceDialParams {
458
615
  timeoutSeconds?: number;
459
616
  };
460
617
  };
618
+ /** Exact-match attributes used only for workspace webhook routing. Requires agentId. */
619
+ webhookTags?: Record<string, string>;
461
620
  /** Free-form metadata round-tripped to your webhooks. */
462
621
  metadata?: Record<string, unknown>;
622
+ /**
623
+ * @deprecated The agent-initiated end_call tool is now always on; the server
624
+ * accepts this field for compat but ignores it.
625
+ */
626
+ endCall?: {
627
+ enabled: boolean;
628
+ };
463
629
  }
464
630
  export interface VoiceDialResult {
465
631
  sessionId: string;
@@ -470,6 +636,64 @@ export interface VoiceDialResult {
470
636
  to: string;
471
637
  from: string;
472
638
  }
639
+ /**
640
+ * One turn from `GET /v1/sessions/:id/transcript` — the lightweight live
641
+ * transcript poll. Note the camelCase keys: this endpoint's serialization
642
+ * differs from the snake_case `CallTranscriptEntry` embedded in `CallDetail`.
643
+ */
644
+ export interface SessionTranscriptEntry {
645
+ id: string;
646
+ index: number;
647
+ source: 'user' | 'agent' | 'system';
648
+ text: string;
649
+ startedAt: string;
650
+ endedAt: string | null;
651
+ provider: string | null;
652
+ model: string | null;
653
+ /** Per-stage latency legs (ms) — null on user/system turns. */
654
+ eouMs: number | null;
655
+ llmTtftMs: number | null;
656
+ ttsTtfbMs: number | null;
657
+ latencyStatus: 'partial' | 'complete' | 'interrupted' | 'error' | null;
658
+ conversationalLatencyMs: number | null;
659
+ /** Tool calls the agent made on this turn (empty when none). */
660
+ toolCalls: {
661
+ name: string;
662
+ args: string;
663
+ }[];
664
+ }
665
+ export interface SessionTranscript {
666
+ entries: SessionTranscriptEntry[];
667
+ }
668
+ /**
669
+ * One push from `sessions.stream()` (SSE under the hood, auto-reconnecting).
670
+ * `end` is always the final event; transport-level reconnects and server
671
+ * stream rotations are handled inside the SDK and never surface here.
672
+ */
673
+ export type SessionStreamEvent = {
674
+ type: 'status';
675
+ status: string;
676
+ endedAt: string | null;
677
+ } | {
678
+ type: 'transcript';
679
+ turn: SessionTranscriptEntry;
680
+ } | {
681
+ type: 'event';
682
+ event: CallEvent;
683
+ } | {
684
+ type: 'end';
685
+ reason: 'session_ended';
686
+ };
687
+ export interface SessionStreamOptions {
688
+ /**
689
+ * Resume position (`"<lastTurnIndex>:<lastEventCreatedAtMs>"`). Rarely
690
+ * needed — the iterator tracks it internally across reconnects; pass it
691
+ * only to resume a NEW iterator after your own process restarted.
692
+ */
693
+ cursor?: string;
694
+ /** Abort to stop streaming (the iterator returns). */
695
+ signal?: AbortSignal;
696
+ }
473
697
  export type PhoneNumberDirection = 'inbound' | 'outbound' | 'both';
474
698
  export type PhoneNumberSource = 'managed' | 'sip_trunk';
475
699
  export type PhoneNumberSmsAssignmentStatus = 'FAILED_ASSIGNMENT' | 'PENDING_ASSIGNMENT' | 'ASSIGNED' | 'PENDING_UNASSIGNMENT' | 'FAILED_UNASSIGNMENT';
@@ -502,16 +726,34 @@ export interface PhoneNumberRow {
502
726
  smsCampaignId: string | null;
503
727
  smsAssignmentStatus: PhoneNumberSmsAssignmentStatus | null;
504
728
  smsAssignmentUpdatedAt: string | null;
729
+ telnyxMessagingProfileId: string | null;
730
+ smsMessagingProfileStatus: 'pending' | 'ready' | 'failed';
731
+ smsMessagingProfileUpdatedAt: string | null;
732
+ smsMessagingProfileError: string | null;
733
+ smsAutomationEnabled: boolean;
505
734
  /**
506
735
  * 1:1 link to a persisted agent. When set, inbound calls hydrate
507
736
  * pipeline config from the agent row instead of (or alongside) the
508
737
  * dispatch_metadata_template.
509
738
  */
510
739
  agentId: string | null;
740
+ /**
741
+ * Inbound destination when this number answers to a HUMAN rather than an
742
+ * agent — the org-defined broker whose softphone is rung. Mutually
743
+ * exclusive with `agentId`: assigning one clears the other, because a number
744
+ * routed to a broker is provisioned so that no agent joins ahead of them.
745
+ */
746
+ routeToBrokerId: string | null;
511
747
  setupStatus: PhoneNumberSetupStatus;
512
748
  nextChargeAt: string;
513
749
  lastChargedAt: string | null;
750
+ /** Effective billing-or-compliance suspension timestamp. */
514
751
  suspendedAt: string | null;
752
+ /** Billing-only suspension, retained independently from compliance review. */
753
+ billingSuspendedAt?: string | null;
754
+ /** Compliance-only suspension for Speko-managed numbers. */
755
+ complianceSuspendedAt?: string | null;
756
+ suspensionReason?: 'billing' | 'compliance' | null;
515
757
  createdAt: string;
516
758
  updatedAt: string;
517
759
  }
@@ -550,6 +792,15 @@ export interface PhoneNumberUpdateParams {
550
792
  label?: string | null;
551
793
  /** Pass `null` to unlink, a string to relink. */
552
794
  agentId?: string | null;
795
+ /**
796
+ * Route inbound calls on this number to a human broker's softphone instead of
797
+ * an agent — pass your org-defined broker id, or `null` to stop. Setting
798
+ * it clears `agentId`, and setting `agentId` clears it; sending both in one
799
+ * request is a validation error. Requires the human-calling feature.
800
+ */
801
+ routeToBrokerId?: string | null;
802
+ /** Owner/admin-only opt-in for inbound SMS agent replies on this number. */
803
+ smsAutomationEnabled?: boolean;
553
804
  }
554
805
  export interface AvailablePhoneNumber {
555
806
  e164: string;
@@ -597,24 +848,56 @@ export interface PhoneNumberKybAuthorizedRepresentative {
597
848
  email: string;
598
849
  phone?: string;
599
850
  }
851
+ export interface PhoneNumberKybDeclaration {
852
+ businessName: string;
853
+ useCase: string;
854
+ }
855
+ export type PhoneNumberKybAttestor = {
856
+ kind: 'user';
857
+ userId: string;
858
+ name: string;
859
+ email: string;
860
+ organizationRole: string | null;
861
+ } | {
862
+ kind: 'api_key';
863
+ apiKeyId: string;
864
+ };
865
+ export interface PhoneNumberKybAttestationContract {
866
+ version: string;
867
+ text: string;
868
+ termsVersion: string;
869
+ termsUrl: string;
870
+ }
600
871
  export interface PhoneNumberKybDraftParams {
601
872
  businessProfile: PhoneNumberKybBusinessProfile;
602
873
  authorizedRepresentative: PhoneNumberKybAuthorizedRepresentative;
603
874
  attestationAccepted?: boolean;
604
875
  }
605
- export interface PhoneNumberKybSubmitParams {
876
+ export type PhoneNumberKybSubmitParams = {
877
+ declaration: PhoneNumberKybDeclaration;
878
+ attestationAccepted: true;
879
+ attestationVersion: string;
880
+ } | {
606
881
  businessProfile: PhoneNumberKybBusinessProfile;
607
882
  authorizedRepresentative: PhoneNumberKybAuthorizedRepresentative;
608
883
  attestationAccepted: true;
609
- }
884
+ attestationVersion?: string;
885
+ };
610
886
  export interface PhoneNumberKybSubmission {
611
887
  id: string;
612
888
  organizationId: string;
613
889
  status: PhoneNumberKybSubmissionStatus;
614
890
  businessProfile: PhoneNumberKybBusinessProfile | null;
615
891
  authorizedRepresentative: PhoneNumberKybAuthorizedRepresentative | null;
892
+ declaration?: PhoneNumberKybDeclaration | null;
893
+ attestor?: PhoneNumberKybAttestor | null;
616
894
  attestationAccepted: boolean;
895
+ attestationVersion?: string | null;
896
+ attestationText?: string | null;
897
+ termsVersion?: string | null;
617
898
  attestedAt: string | null;
899
+ accessHoldAt?: string | null;
900
+ accessHoldReason?: 'rejected' | 'revoked' | null;
618
901
  submittedByUserId: string | null;
619
902
  submittedByEmail: string | null;
620
903
  submittedByApiKeyId: string | null;
@@ -632,6 +915,10 @@ export interface PhoneNumberKybSubmission {
632
915
  export interface PhoneNumberKybOverview {
633
916
  status: PhoneNumberKybStatus;
634
917
  submission: PhoneNumberKybSubmission | null;
918
+ declarationPrefill?: PhoneNumberKybDeclaration;
919
+ requiredAttestation?: PhoneNumberKybAttestationContract;
920
+ attestationRequired?: boolean;
921
+ complianceAccess?: 'enabled' | 'awaiting_attestation' | 'suspended';
635
922
  prefill: {
636
923
  businessProfile: PhoneNumberKybBusinessProfile;
637
924
  authorizedRepresentative: PhoneNumberKybAuthorizedRepresentative;
@@ -663,6 +950,19 @@ export interface AgentStackPreferences {
663
950
  export interface AgentSttOptions {
664
951
  /** Vocabulary keywords forwarded to whichever STT provider the router picks. */
665
952
  keywords?: string[];
953
+ /**
954
+ * Free-text transcription context (domain, names, expected phrases), max
955
+ * 2000 chars. Honored only by prompt-capable STT models (OpenAI
956
+ * gpt-4o-transcribe family, AssemblyAI Universal-3 Pro tiers).
957
+ */
958
+ prompt?: string;
959
+ /**
960
+ * STT stream-language override. A BCP-47-ish tag ('en', 'es-MX'), a
961
+ * provider keyword like Deepgram's 'multi', or 'auto' to let the provider
962
+ * detect the spoken language itself (Soniox auto-detects when hints are
963
+ * omitted). Never affects stack routing — that keeps the agent language.
964
+ */
965
+ language?: string;
666
966
  }
667
967
  /**
668
968
  * Built-in ambience clip ids supported by the hosted worker. Custom clip
@@ -670,7 +970,7 @@ export interface AgentSttOptions {
670
970
  * keeps the v1 API simple and lets the worker map straight to the
671
971
  * `BuiltinAudioClip` enum.
672
972
  */
673
- export type AgentAmbientClip = 'office-ambience' | 'keyboard-typing' | 'keyboard-typing2';
973
+ export type AgentAmbientClip = 'office-ambience' | 'city-ambience' | 'forest-ambience' | 'crowded-room' | 'keyboard-typing' | 'keyboard-typing2';
674
974
  /**
675
975
  * Per-agent background audio. Today only ambient (continuous loop) is
676
976
  * supported. The ambience plays on a separate media track mixed
@@ -680,7 +980,14 @@ export type AgentAmbientClip = 'office-ambience' | 'keyboard-typing' | 'keyboard
680
980
  export interface AgentBackgroundAudio {
681
981
  ambient?: {
682
982
  clip: AgentAmbientClip;
683
- /** Linear gain in [0, 1]. Defaults to 1.0 (clip's natural level). */
983
+ /**
984
+ * Linear gain in `[0, 16]`, defaulting to 1.0 — the clip's own recorded
985
+ * level, which is not the same as "full volume". The built-in clips are
986
+ * mastered roughly 30 dB apart, so the useful range differs per clip:
987
+ * `office-ambience` is very quiet (about -52 LUFS) and needs ~5-10 to sit
988
+ * audibly under speech, `city-ambience` is about right at 1, and
989
+ * `crowded-room` is loud enough that it distorts past ~1.6.
990
+ */
684
991
  volume?: number;
685
992
  };
686
993
  }
@@ -688,46 +995,312 @@ export interface AgentSpeechNormalization {
688
995
  pronunciationDictionary?: Record<string, string>;
689
996
  textReplacements?: Record<string, string>;
690
997
  }
998
+ /**
999
+ * A caller-defined post-call extraction field. Only meaningful on the
1000
+ * `postCall` webhook: the call-analysis pass fills each from the transcript per
1001
+ * `description`, typed by `type`, and the values are delivered under the
1002
+ * webhook payload's top-level `custom_data` object keyed by `name`. `options`
1003
+ * is required for `enum` fields.
1004
+ */
1005
+ export interface AgentExtractionField {
1006
+ /**
1007
+ * Stable key the value lands under in `custom_data`. Must be a valid
1008
+ * identifier (`^[a-zA-Z_][a-zA-Z0-9_]*$`), up to 64 chars, unique per webhook.
1009
+ */
1010
+ name: string;
1011
+ type: 'string' | 'number' | 'boolean' | 'enum';
1012
+ /**
1013
+ * Instruction the LLM uses to extract this field. 1 to 10,000 characters,
1014
+ * and at most 40,000 characters combined across all fields on the webhook.
1015
+ */
1016
+ description: string;
1017
+ /**
1018
+ * Allowed values — required (and only valid) when `type` is `'enum'`.
1019
+ * 1–50 options, each up to 120 characters.
1020
+ */
1021
+ options?: string[];
1022
+ }
1023
+ /**
1024
+ * Outbound auth header input — `value` is the plaintext credential Speko
1025
+ * encrypts at rest. Required on create; omit on update to keep the value
1026
+ * already stored under this header's ref.
1027
+ */
1028
+ export interface AgentWebhookAuthHeaderInput {
1029
+ name: string;
1030
+ value?: string;
1031
+ }
1032
+ /** Outbound auth header as returned by the API — value stays server-side. */
1033
+ export interface AgentWebhookAuthHeader {
1034
+ name: string;
1035
+ secretRef: string;
1036
+ }
691
1037
  export interface AgentLifecycleWebhookCreate {
692
1038
  url: string;
693
- /** Deprecated. Lifecycle webhooks use the org-level signing secret from API keys. */
1039
+ /**
1040
+ * Optional per-webhook signing secret. When supplied, this endpoint signs
1041
+ * with its own secret instead of the shared org-level secret from API keys.
1042
+ */
694
1043
  secret?: string;
695
1044
  headers?: Record<string, string>;
1045
+ /** Secret-referenced outbound auth headers (e.g. a Bearer token your endpoint requires). */
1046
+ authHeaders?: AgentWebhookAuthHeaderInput[];
696
1047
  timeoutMs?: number;
697
1048
  responseMode?: 'sync' | 'async';
698
1049
  asyncAck?: string;
1050
+ /** Post-call data-extraction fields. Applies to the `postCall` webhook only. */
1051
+ extractionFields?: AgentExtractionField[];
699
1052
  }
700
1053
  export interface AgentLifecycleWebhookUpdate {
701
1054
  url: string;
702
- /** Deprecated. Lifecycle webhooks use the org-level signing secret from API keys. */
1055
+ /**
1056
+ * Optional per-webhook signing secret. Supply to set/rotate a per-webhook
1057
+ * secret; omit to keep the existing (or shared org-level) secret.
1058
+ */
703
1059
  secret?: string;
704
1060
  headers?: Record<string, string>;
1061
+ /** Secret-referenced outbound auth headers. Replaces the stored set; omit a `value` to keep it. */
1062
+ authHeaders?: AgentWebhookAuthHeaderInput[];
705
1063
  timeoutMs?: number;
706
1064
  responseMode?: 'sync' | 'async';
707
1065
  asyncAck?: string;
1066
+ /** Post-call data-extraction fields. Applies to the `postCall` webhook only. */
1067
+ extractionFields?: AgentExtractionField[];
708
1068
  }
709
1069
  export interface AgentLifecycleWebhookSerialized {
710
1070
  url: string;
711
1071
  secretRef: string;
712
1072
  headers?: Record<string, string>;
1073
+ /** Outbound auth-header pointers; values stay encrypted server-side. */
1074
+ authHeaders?: AgentWebhookAuthHeader[];
713
1075
  timeoutMs?: number;
714
1076
  responseMode?: 'sync' | 'async';
715
1077
  asyncAck?: string;
1078
+ /** Post-call data-extraction fields. Present on the `postCall` webhook only. */
1079
+ extractionFields?: AgentExtractionField[];
716
1080
  }
717
1081
  export interface AgentWebhooksSerialized {
718
1082
  preCall?: AgentLifecycleWebhookSerialized;
719
1083
  postCall?: AgentLifecycleWebhookSerialized;
720
1084
  status?: AgentLifecycleWebhookSerialized;
1085
+ /** Dedicated `call.analysis` webhook — LLM analysis results only. */
1086
+ analysis?: AgentLifecycleWebhookSerialized;
1087
+ /** Dedicated `call.recording` webhook — fires when the recording turns terminal. */
1088
+ recording?: AgentLifecycleWebhookSerialized;
721
1089
  }
722
1090
  export interface AgentWebhooksCreate {
723
1091
  preCall?: AgentLifecycleWebhookCreate;
724
1092
  postCall?: AgentLifecycleWebhookCreate;
725
1093
  status?: AgentLifecycleWebhookCreate;
1094
+ /**
1095
+ * Dedicated `call.analysis` webhook. Delivered once per call when the LLM
1096
+ * analysis completes: summary, outcome, structured_data, and custom_data —
1097
+ * without the transcript/cost/recording of the combined `call.report`.
1098
+ */
1099
+ analysis?: AgentLifecycleWebhookCreate;
1100
+ /**
1101
+ * Dedicated `call.recording` webhook. Delivered once per call when the
1102
+ * recording reaches a terminal state — `ready` carries the presigned
1103
+ * `recording_url` (7-day TTL), `failed` carries `recording_url: null`.
1104
+ */
1105
+ recording?: AgentLifecycleWebhookCreate;
726
1106
  }
727
1107
  export interface AgentWebhooksUpdate {
728
1108
  preCall?: AgentLifecycleWebhookUpdate | null;
729
1109
  postCall?: AgentLifecycleWebhookUpdate | null;
730
1110
  status?: AgentLifecycleWebhookUpdate | null;
1111
+ analysis?: AgentLifecycleWebhookUpdate | null;
1112
+ recording?: AgentLifecycleWebhookUpdate | null;
1113
+ }
1114
+ /**
1115
+ * Everything a workspace webhook endpoint can subscribe to.
1116
+ *
1117
+ * Two families, and they behave differently on the wire:
1118
+ *
1119
+ * **AI voice-session events** (`call.pre_call` … `call.recording`) describe one
1120
+ * `voice_session` as it progresses, and carry a `session_id`.
1121
+ *
1122
+ * **Programmable-voice control events** (`call.initiated` … `call.hangup`) are
1123
+ * the webhook projection of the human-calling event stream — the same events
1124
+ * {@link CallControl.events} returns. A human call is not a `voice_session`, so
1125
+ * there is no session id to correlate on: the payload carries `call_id`,
1126
+ * `control_id` (null for call-scoped events that belong to no single leg),
1127
+ * `event_id` and `occurred_at`, merged with the event's own payload, and
1128
+ * `call_id` is the correlation key.
1129
+ *
1130
+ * Control events are delivered **once, without automatic retry**. Only
1131
+ * `call.report`, `call.analysis` and `call.recording` are durable — for those, a
1132
+ * failed delivery is re-attempted on a backoff. A control event that misses its
1133
+ * endpoint is gone from the webhook feed; the call's own event history
1134
+ * ({@link CallControl.events}) is the durable record, so reconcile from there
1135
+ * rather than treating the webhook as a queue.
1136
+ */
1137
+ export type WorkspaceWebhookEventType = 'call.pre_call' | 'call.status' | 'call.report' | 'call.analysis' | 'call.recording' | 'call.initiated' | 'call.ringing' | 'call.answered' | 'call.bridged' | 'call.hold' | 'call.unhold' | 'call.mute' | 'call.unmute' | 'call.dtmf.sent' | 'call.transfer.initiated' | 'call.transfer.completed' | 'call.transfer.failed' | 'call.leg.hangup' | 'call.hangup' | 'sms.received' | 'sms.accepted' | 'sms.sent' | 'sms.delivered' | 'sms.delivery_failed' | 'sms.submission_unknown' | 'sms.opted_out' | 'sms.opted_in';
1138
+ export type WebhookEventType = WorkspaceWebhookEventType | 'imessage.received' | 'imessage.reaction_received' | 'imessage.sent' | 'imessage.delivered' | 'imessage.delivery_failed';
1139
+ export type WebhookDeliveryStatus = 'pending' | 'delivering' | 'succeeded' | 'failed' | 'cancelled' | 'expired';
1140
+ export interface WebhookEndpointAuthHeaderInput {
1141
+ name: string;
1142
+ /** Write-only plaintext. The server encrypts it and never returns it. */
1143
+ value: string;
1144
+ }
1145
+ export interface WebhookEndpointAuthHeaderUpdate {
1146
+ name: string;
1147
+ /** Supply to set or rotate; omit to retain the stored value for this header name. */
1148
+ value?: string;
1149
+ }
1150
+ export interface WebhookEndpointInput {
1151
+ name: string;
1152
+ url: string;
1153
+ events: WorkspaceWebhookEventType[];
1154
+ /** Defaults to true. When false, agentIds must contain at least one agent. */
1155
+ allAgents?: boolean;
1156
+ agentIds?: string[];
1157
+ filterTags?: Record<string, string>;
1158
+ headers?: Record<string, string>;
1159
+ authHeaders?: WebhookEndpointAuthHeaderInput[];
1160
+ timeoutMs?: number;
1161
+ signingSecretSource?: 'workspace' | 'custom';
1162
+ /** Write-only. Required when signingSecretSource is custom. */
1163
+ signingSecret?: string;
1164
+ extractionFields?: AgentExtractionField[];
1165
+ /** Include message text, or emit metadata-only SMS payloads. Defaults to full. */
1166
+ contentMode?: 'full' | 'metadata_only';
1167
+ }
1168
+ export type WebhookEndpointUpdate = Partial<Omit<WebhookEndpointInput, 'authHeaders'>> & {
1169
+ authHeaders?: WebhookEndpointAuthHeaderUpdate[];
1170
+ };
1171
+ export interface WebhookEndpoint {
1172
+ id: string;
1173
+ name: string;
1174
+ url: string;
1175
+ events: WorkspaceWebhookEventType[];
1176
+ allAgents: boolean;
1177
+ agentIds: string[];
1178
+ filterTags: Record<string, string>;
1179
+ headers: Record<string, string>;
1180
+ authHeaders: Array<{
1181
+ name: string;
1182
+ configured: true;
1183
+ }>;
1184
+ timeoutMs: number;
1185
+ signingSecretSource: 'workspace' | 'custom';
1186
+ hasCustomSigningSecret: boolean;
1187
+ extractionFields: AgentExtractionField[];
1188
+ contentMode: 'full' | 'metadata_only';
1189
+ legacyManaged: boolean;
1190
+ createdAt: string;
1191
+ updatedAt: string;
1192
+ }
1193
+ export interface WebhookDeliveryListParams {
1194
+ endpointId?: string;
1195
+ event?: WebhookEventType;
1196
+ agentId?: string;
1197
+ status?: WebhookDeliveryStatus;
1198
+ sessionId?: string;
1199
+ eventId?: string;
1200
+ from?: string;
1201
+ to?: string;
1202
+ cursor?: string;
1203
+ limit?: number;
1204
+ }
1205
+ export interface WebhookDelivery {
1206
+ id: string;
1207
+ eventId: string;
1208
+ endpointId: string;
1209
+ endpointName: string;
1210
+ endpointKind: 'workspace' | 'imessage';
1211
+ endpointDeleted: boolean;
1212
+ event: WebhookEventType;
1213
+ sessionId: string | null;
1214
+ agentId: string | null;
1215
+ webhookTags: Record<string, string>;
1216
+ status: WebhookDeliveryStatus;
1217
+ attempts: number;
1218
+ httpStatus: number | null;
1219
+ error: string | null;
1220
+ occurredAt: string;
1221
+ expiresAt: string;
1222
+ deliveredAt: string | null;
1223
+ createdAt: string;
1224
+ /** False for provider-retried iMessage subscriber deliveries. */
1225
+ canRedeliver: boolean;
1226
+ }
1227
+ export interface WebhookDeliveryEndpointOption {
1228
+ id: string;
1229
+ name: string;
1230
+ kind: 'workspace' | 'imessage';
1231
+ deleted: boolean;
1232
+ }
1233
+ export interface WebhookDeliveryPage {
1234
+ data: WebhookDelivery[];
1235
+ nextCursor: string | null;
1236
+ endpointOptions: WebhookDeliveryEndpointOption[];
1237
+ }
1238
+ export interface WebhookDeliveryAttempt {
1239
+ id: string;
1240
+ attemptNumber: number;
1241
+ trigger: 'automatic' | 'manual';
1242
+ requestUrl: string;
1243
+ requestHeaders: Record<string, string>;
1244
+ requestBody: Record<string, unknown>;
1245
+ responseStatus: number | null;
1246
+ responseBody: string | null;
1247
+ responseTruncated: boolean;
1248
+ durationMs: number;
1249
+ error: string | null;
1250
+ createdAt: string;
1251
+ }
1252
+ export interface WebhookDeliveryDetail extends Omit<WebhookDelivery, 'attempts'> {
1253
+ attemptCount: number;
1254
+ requestPayload: Record<string, unknown>;
1255
+ attempts: WebhookDeliveryAttempt[];
1256
+ }
1257
+ /**
1258
+ * One prompt-variable registry entry. `defaultValue` fills the variable when a
1259
+ * session/dial call omits it (empty string = declared optional: renders blank
1260
+ * and `{% if %}` branches false). Without a default the variable is required
1261
+ * per call — omitting it fails session create with 400
1262
+ * `MISSING_TEMPLATE_VARIABLES`. Names may not use the reserved `system.`
1263
+ * namespace.
1264
+ */
1265
+ export interface AgentPromptVariable {
1266
+ name: string;
1267
+ defaultValue?: string;
1268
+ description?: string;
1269
+ }
1270
+ /** Turn-taking configuration for cascaded agents. Realtime agents ignore VAD. */
1271
+ export interface AgentTurnHandling {
1272
+ /** Local VAD provider. Omit to use Silero. */
1273
+ vad?: {
1274
+ provider: 'silero' | 'ai-coustics';
1275
+ };
1276
+ /**
1277
+ * Caller-leg input enhancement (ai-coustics). Omit for the platform default;
1278
+ * `enabled: false` turns it off; `model` is plain Quail (default) or Quail Voice
1279
+ * Focus (primary-speaker isolation, explicit opt-in).
1280
+ */
1281
+ noiseCancellation?: {
1282
+ enabled: boolean;
1283
+ model?: 'quail' | 'quail-voice-focus';
1284
+ };
1285
+ profile?: 'conversational' | 'ivr' | 'ivr_patient';
1286
+ endpointing?: {
1287
+ minDelay?: number;
1288
+ maxDelay?: number;
1289
+ };
1290
+ interruption?: {
1291
+ mode?: 'adaptive' | 'vad';
1292
+ minDuration?: number;
1293
+ minWords?: number;
1294
+ };
1295
+ turnDetection?: boolean | 'stt';
1296
+ contextThreshold?: boolean;
1297
+ textGate?: boolean;
1298
+ turnDetector?: 'smart_turn' | 'speko_turn_v1';
1299
+ dtmfToolDescription?: string;
1300
+ amdPrompt?: string;
1301
+ waitForCallee?: boolean;
1302
+ onMachine?: 'hangup' | 'leave_message' | 'agent_decides';
1303
+ voicemailMessage?: string;
731
1304
  }
732
1305
  export interface AgentRow {
733
1306
  id: string;
@@ -741,7 +1314,17 @@ export interface AgentRow {
741
1314
  sttOptions: AgentSttOptions | null;
742
1315
  backgroundAudio: AgentBackgroundAudio | null;
743
1316
  speechNormalization: AgentSpeechNormalization | null;
1317
+ turnHandling: AgentTurnHandling | null;
1318
+ /** @deprecated Use organization-owned `speko.webhooks` endpoints. */
744
1319
  webhooks: AgentWebhooksSerialized | null;
1320
+ /**
1321
+ * Post-call extraction schema on the agent itself — no webhook required.
1322
+ * Merged with `webhooks.postCall.extractionFields`; the agent-level
1323
+ * definition wins on a name collision.
1324
+ */
1325
+ extractionFields: AgentExtractionField[];
1326
+ /** Prompt-variable registry. Returned on single-agent reads; null = empty. */
1327
+ promptVariables?: AgentPromptVariable[] | null;
745
1328
  createdAt: string;
746
1329
  updatedAt: string;
747
1330
  }
@@ -755,10 +1338,30 @@ export interface AgentCreateParams {
755
1338
  sttOptions?: AgentSttOptions;
756
1339
  backgroundAudio?: AgentBackgroundAudio;
757
1340
  speechNormalization?: AgentSpeechNormalization;
1341
+ turnHandling?: AgentTurnHandling;
1342
+ /** @deprecated Use `speko.webhooks.create()` after creating the agent. */
758
1343
  webhooks?: AgentWebhooksCreate;
759
- }
760
- export type AgentUpdateParams = Partial<Omit<AgentCreateParams, 'webhooks'>> & {
1344
+ /**
1345
+ * Post-call extraction schema on the agent itself — no webhook required.
1346
+ * Merged with `webhooks.postCall.extractionFields`; the agent-level
1347
+ * definition wins on a name collision.
1348
+ */
1349
+ extractionFields?: AgentExtractionField[];
1350
+ /** Declare the prompt's `{{variables}}` with per-agent defaults/descriptions. */
1351
+ promptVariables?: AgentPromptVariable[];
1352
+ }
1353
+ export type AgentUpdateParams = Partial<Omit<AgentCreateParams, 'webhooks' | 'turnHandling'>> & {
1354
+ /** Set to null to clear all stored turn-taking overrides. */
1355
+ turnHandling?: AgentTurnHandling | null;
1356
+ /** @deprecated Use `speko.webhooks.update()` for organization-owned endpoints. */
761
1357
  webhooks?: AgentWebhooksUpdate | null;
1358
+ /**
1359
+ * Post-call extraction schema on the agent itself — no webhook required.
1360
+ * Merged with `webhooks.postCall.extractionFields`; the agent-level
1361
+ * definition wins on a name collision.
1362
+ */
1363
+ /** `null` clears the schema. */
1364
+ extractionFields?: AgentExtractionField[] | null;
762
1365
  };
763
1366
  export interface CallTranscriptEntry {
764
1367
  id: string;
@@ -770,6 +1373,11 @@ export interface CallTranscriptEntry {
770
1373
  provider: string | null;
771
1374
  model: string | null;
772
1375
  metadata: Record<string, unknown>;
1376
+ eou_ms?: number | null;
1377
+ llm_ttft_ms?: number | null;
1378
+ tts_ttfb_ms?: number | null;
1379
+ latency_status?: 'partial' | 'complete' | 'interrupted' | 'error' | null;
1380
+ conversational_latency_ms?: number | null;
773
1381
  }
774
1382
  export interface CallCostLine {
775
1383
  provider: string;
@@ -778,12 +1386,26 @@ export interface CallCostLine {
778
1386
  keySource: KeySource;
779
1387
  costMicroUsd: string;
780
1388
  }
1389
+ export interface CallReportWebhookDelivery {
1390
+ endpointId: string;
1391
+ deliveryId: string;
1392
+ eventId: string;
1393
+ delivered: boolean;
1394
+ status: number | null;
1395
+ error: string | null;
1396
+ createdAt: string;
1397
+ }
781
1398
  export interface CallReport {
782
1399
  session_id: string;
783
1400
  organization_id: string;
784
1401
  summary: string;
785
1402
  outcome: string;
786
1403
  structured_data: Record<string, unknown>;
1404
+ /**
1405
+ * Caller-defined extraction values, keyed by field name — the same object the
1406
+ * `call.report` webhook delivers. `{}` when the agent declares no fields.
1407
+ */
1408
+ custom_data: Record<string, unknown>;
787
1409
  transcript: {
788
1410
  entries: CallTranscriptEntry[];
789
1411
  };
@@ -797,11 +1419,18 @@ export interface CallReport {
797
1419
  analysis_model: string | null;
798
1420
  analysis_error: string | null;
799
1421
  analysis_completed_at: string | null;
1422
+ /** @deprecated Aggregate retained through the current SDK major version. */
800
1423
  post_call_webhook_status: 'not_configured' | 'pending' | 'delivered' | 'failed';
1424
+ /** @deprecated Aggregate retained through the current SDK major version. */
801
1425
  post_call_webhook_attempts: number;
1426
+ /** @deprecated Aggregate retained through the current SDK major version. */
802
1427
  post_call_webhook_next_retry_at: string | null;
1428
+ /** @deprecated Aggregate retained through the current SDK major version. */
803
1429
  post_call_webhook_delivered_at: string | null;
1430
+ /** @deprecated Aggregate retained through the current SDK major version. */
804
1431
  post_call_webhook_error: string | null;
1432
+ /** Canonical per-endpoint results; singular post-call fields are deprecated aggregates. */
1433
+ webhook_deliveries: CallReportWebhookDelivery[];
805
1434
  created_at: string;
806
1435
  updated_at: string;
807
1436
  }
@@ -846,11 +1475,35 @@ export interface FinalizeCallReportResult {
846
1475
  summary: string;
847
1476
  outcome: string;
848
1477
  cost_micro_usd: string;
1478
+ /** @deprecated Aggregate retained through the current SDK major version. */
849
1479
  webhook: unknown;
1480
+ webhook_deliveries: CallReportWebhookDelivery[];
850
1481
  }
851
1482
  export interface CallRecording {
852
1483
  url: string;
853
1484
  }
1485
+ export interface WebJoinParams {
1486
+ /** Display name other participants (and transcripts) see for the joiner. */
1487
+ displayName?: string;
1488
+ }
1489
+ export interface WebJoinResult {
1490
+ /** LiveKit access token for the live call's room. Mint at click time — short TTL. */
1491
+ token: string;
1492
+ /** Public LiveKit URL the browser connects to (pass both to `@spekoai/client`). */
1493
+ url: string;
1494
+ /** Participant identity minted for this join (unique per join). */
1495
+ identity: string;
1496
+ roomName: string;
1497
+ /** ISO timestamp the token stops being accepted for NEW connections. */
1498
+ expiresAt: string;
1499
+ }
1500
+ export interface EndCallResult {
1501
+ ok: true;
1502
+ /** `ending` when teardown was requested; `already_ended` when the call was over. */
1503
+ status: 'ending' | 'already_ended';
1504
+ /** ISO timestamp, present only with `already_ended`. */
1505
+ ended_at?: string;
1506
+ }
854
1507
  export interface CallEvent {
855
1508
  id: string;
856
1509
  session_id: string | null;
@@ -1002,6 +1655,44 @@ export interface AgentCallListPage {
1002
1655
  export interface AgentToolSourceInline {
1003
1656
  kind: 'inline';
1004
1657
  }
1658
+ /**
1659
+ * HTTP verb for a webhook tool. Omitting it means `POST`, so a tool
1660
+ * written before this field existed is unchanged.
1661
+ *
1662
+ * `GET` and `DELETE` send NO request body — not a `body` template and not
1663
+ * the default envelope either. Pairing one with `body` is rejected with
1664
+ * `422 WEBHOOK_TEMPLATE_INVALID` rather than silently dropping the body.
1665
+ */
1666
+ export type AgentToolWebhookMethod = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'GET';
1667
+ /**
1668
+ * JSON body template for a webhook tool.
1669
+ *
1670
+ * Omit it and Speko sends its fixed envelope —
1671
+ * `{tool, args, idempotency_key, session_id, tool_call_id}` — unchanged.
1672
+ * Supply one and it REPLACES that envelope, so a tool can post the shape
1673
+ * a third-party API actually wants.
1674
+ *
1675
+ * Every string leaf interpolates `{{name}}`: any session `variables` or
1676
+ * `toolSecrets` entry, plus these reserved names —
1677
+ *
1678
+ * | Name | Substitutes |
1679
+ * | --------------------- | ---------------------------------------------------- |
1680
+ * | `{{tool_name}}` | the tool's name |
1681
+ * | `{{session_id}}` | the session id |
1682
+ * | `{{tool_call_id}}` | the model's tool-call id |
1683
+ * | `{{idempotency_key}}` | `<session_id>:<tool_call_id>` |
1684
+ * | `{{args.<name>}}` | one argument, JSON type preserved |
1685
+ * | `{{args}}` | the whole arguments object — whole values only |
1686
+ *
1687
+ * Reserved names win over a session value of the same name. `{{args}}`
1688
+ * must be the ENTIRE value of a key; embedding it in a longer string is
1689
+ * rejected at write time instead of being JSON-stringified into it.
1690
+ *
1691
+ * Limits: 8 KB serialized, 8 levels of nesting, and no `__proto__`,
1692
+ * `constructor` or `prototype` key. A URL whose ORIGIN is templated may
1693
+ * not carry a body template at all — see the tool-calling guide.
1694
+ */
1695
+ export type AgentToolWebhookBody = Record<string, unknown> | unknown[];
1005
1696
  /**
1006
1697
  * Webhook source as sent to {@link AgentTools.create}. The plaintext
1007
1698
  * `secret` is encrypted server-side; the returned row carries
@@ -1013,6 +1704,12 @@ export interface AgentToolSourceWebhookCreate {
1013
1704
  /** Plaintext shared secret. Encrypted server-side at write time. */
1014
1705
  secret: string;
1015
1706
  headers?: Record<string, string>;
1707
+ /** Secret-referenced outbound auth headers (e.g. a Bearer token your endpoint requires). */
1708
+ authHeaders?: AgentWebhookAuthHeaderInput[];
1709
+ /** HTTP verb. Omit for `POST`. `GET`/`DELETE` send no body and reject `body`. */
1710
+ method?: AgentToolWebhookMethod;
1711
+ /** JSON body template replacing the default envelope. See {@link AgentToolWebhookBody}. */
1712
+ body?: AgentToolWebhookBody;
1016
1713
  timeoutMs?: number;
1017
1714
  }
1018
1715
  /**
@@ -1025,6 +1722,12 @@ export interface AgentToolSourceWebhookSerialized {
1025
1722
  /** Pointer into Speko's secrets store. */
1026
1723
  secretRef: string;
1027
1724
  headers?: Record<string, string>;
1725
+ /** Outbound auth-header pointers; values stay encrypted server-side. */
1726
+ authHeaders?: AgentWebhookAuthHeader[];
1727
+ /** Absent means `POST`. */
1728
+ method?: AgentToolWebhookMethod;
1729
+ /** The stored body template. Configuration, not a secret — returned as saved. */
1730
+ body?: AgentToolWebhookBody;
1028
1731
  timeoutMs?: number;
1029
1732
  }
1030
1733
  export interface AgentToolSourceBuiltin {
@@ -1056,6 +1759,12 @@ export interface AgentToolSourceWebhookUpdate {
1056
1759
  /** Plaintext shared secret. Omit to keep the existing stored secret; supply to rotate. */
1057
1760
  secret?: string;
1058
1761
  headers?: Record<string, string>;
1762
+ /** Secret-referenced outbound auth headers. Replaces the stored set; omit a `value` to keep it. */
1763
+ authHeaders?: AgentWebhookAuthHeaderInput[];
1764
+ /** HTTP verb. Omit for `POST`. `GET`/`DELETE` send no body and reject `body`. */
1765
+ method?: AgentToolWebhookMethod;
1766
+ /** JSON body template replacing the default envelope. See {@link AgentToolWebhookBody}. */
1767
+ body?: AgentToolWebhookBody;
1059
1768
  timeoutMs?: number;
1060
1769
  }
1061
1770
  export type AgentToolSourceCreate = AgentToolSourceInline | AgentToolSourceWebhookCreate | AgentToolSourceBuiltin | AgentToolSourceIntegration;
@@ -1068,6 +1777,8 @@ export interface AgentToolRow {
1068
1777
  description: string;
1069
1778
  parameters: Record<string, unknown>;
1070
1779
  source: AgentToolSourceSerialized;
1780
+ /** Spoken lead-in behavior before this tool executes. */
1781
+ preToolSpeech: ChatToolPreToolSpeech;
1071
1782
  createdAt: string;
1072
1783
  updatedAt: string;
1073
1784
  }
@@ -1076,11 +1787,14 @@ export interface AgentToolCreateParams {
1076
1787
  description: string;
1077
1788
  parameters: Record<string, unknown>;
1078
1789
  source: AgentToolSourceCreate;
1790
+ /** Spoken lead-in behavior before the tool executes. Defaults to `auto`. */
1791
+ preToolSpeech?: ChatToolPreToolSpeech;
1079
1792
  }
1080
1793
  export interface AgentToolUpdateParams {
1081
1794
  description?: string;
1082
1795
  parameters?: Record<string, unknown>;
1083
1796
  source?: AgentToolSourceUpdate;
1797
+ preToolSpeech?: ChatToolPreToolSpeech;
1084
1798
  }
1085
1799
  export interface KnowledgeBaseRow {
1086
1800
  id: string;
@@ -1153,4 +1867,279 @@ export interface KnowledgeBaseDocumentPollOptions {
1153
1867
  /** Total timeout in milliseconds. Default 120000 (2 min). */
1154
1868
  timeoutMs?: number;
1155
1869
  }
1870
+ /**
1871
+ * Parameters for {@link CallControl.dial} — an outbound PSTN call placed by a
1872
+ * human broker, not by an AI agent. For an agent dial see
1873
+ * {@link VoiceDialParams}.
1874
+ */
1875
+ export interface CallControlDialParams {
1876
+ /** Destination in E.164 format (e.g. "+12015551234"). */
1877
+ to: string;
1878
+ /**
1879
+ * Caller ID to present, E.164. Must be a number your org owns; falls back to
1880
+ * the org's default outbound number when omitted.
1881
+ */
1882
+ from?: string;
1883
+ /** Opaque key/values stored on the call and echoed back on every read. */
1884
+ metadata?: Record<string, unknown>;
1885
+ }
1886
+ /**
1887
+ * What {@link CallControl.dial} resolves to. The join credentials come back
1888
+ * *with* the call rather than from a second request, because the dialing
1889
+ * broker's softphone has to already be in the room when the far end answers —
1890
+ * fetch them afterwards and the first moments of the call are silence.
1891
+ *
1892
+ * Destructure both halves; `call` alone is not enough to be heard:
1893
+ *
1894
+ * ```ts
1895
+ * const { call, join } = await speko.callControl.dial({ to: '+12015551234' });
1896
+ * ```
1897
+ */
1898
+ export interface CallControlDialResult {
1899
+ /** The call and both of its legs — the `controlId`s every later command needs. */
1900
+ readonly call: CallResource;
1901
+ /** Room credentials for the dialing broker's own browser leg. */
1902
+ readonly join: CallJoinCredentials;
1903
+ }
1904
+ /** Filters for {@link CallControl.list}. All optional; all AND-ed together. */
1905
+ export interface CallControlListParams {
1906
+ /**
1907
+ * Typed against the contract's `CallStatus` on purpose: the server validates
1908
+ * `?status=` against the same enum and rejects anything else, so a typo is a
1909
+ * compile error here instead of a `VALIDATION_ERROR` at runtime.
1910
+ */
1911
+ status?: CallStatus;
1912
+ direction?: CallDirection;
1913
+ /**
1914
+ * Only calls with a browser leg owned by this broker. Unlike dialing, reading
1915
+ * another broker's calls is allowed — a supervisor view is a legitimate use of
1916
+ * an org-scoped credential.
1917
+ */
1918
+ brokerId?: string;
1919
+ /** Newest first. Server-side default and cap apply. */
1920
+ limit?: number;
1921
+ }
1922
+ export type SmsMessageStatus = 'queued' | 'scheduled' | 'submitting' | 'accepted' | 'sent' | 'delivered' | 'delivery_failed' | 'rejected' | 'submission_unknown' | 'canceled' | 'received';
1923
+ export type SmsMessageDirection = 'inbound' | 'outbound';
1924
+ export type SmsMessageOrigin = 'api' | 'dashboard' | 'agent_tool' | 'agent_auto_reply' | 'telnyx';
1925
+ export interface SmsSegmentEstimate {
1926
+ readonly encoding: 'gsm7' | 'ucs2';
1927
+ readonly segments: number;
1928
+ readonly units: number;
1929
+ readonly per_segment: number;
1930
+ }
1931
+ export interface SmsMessage {
1932
+ readonly id: string;
1933
+ readonly conversation_id: string;
1934
+ readonly batch_id: string | null;
1935
+ readonly from_phone_number_id: string;
1936
+ readonly direction: SmsMessageDirection;
1937
+ readonly origin: SmsMessageOrigin;
1938
+ readonly from: string;
1939
+ readonly to: string;
1940
+ readonly text: string | null;
1941
+ readonly campaign_id: string | null;
1942
+ readonly brand_id: string | null;
1943
+ readonly campaign_snapshot: Record<string, unknown> | null;
1944
+ readonly consent_id: string | null;
1945
+ readonly consent_basis: string | null;
1946
+ readonly recipient_timezone: string | null;
1947
+ readonly requested_send_at: string | null;
1948
+ readonly effective_send_at: string | null;
1949
+ readonly terminal_at: string | null;
1950
+ readonly status: SmsMessageStatus;
1951
+ readonly provider_status: string | null;
1952
+ readonly encoding: 'gsm7' | 'ucs2' | null;
1953
+ readonly estimated_segments: number;
1954
+ readonly segment_count: number;
1955
+ readonly estimated: SmsSegmentEstimate;
1956
+ readonly charged_micro_usd: string;
1957
+ readonly provider_cost_micro_usd: string | null;
1958
+ readonly metadata: Record<string, unknown>;
1959
+ readonly error: {
1960
+ readonly code: string;
1961
+ readonly detail: string | null;
1962
+ } | null;
1963
+ readonly created_at: string;
1964
+ readonly updated_at: string;
1965
+ }
1966
+ export interface SmsPage<T> {
1967
+ readonly data: T[];
1968
+ readonly next_cursor: string | null;
1969
+ }
1970
+ export interface SmsSendParams {
1971
+ readonly from_phone_number_id: string;
1972
+ readonly to: string;
1973
+ readonly text: string;
1974
+ readonly idempotencyKey: string;
1975
+ readonly send_at?: string;
1976
+ readonly consent_id?: string | null;
1977
+ readonly recipient_timezone?: string | null;
1978
+ readonly metadata?: Record<string, unknown>;
1979
+ }
1980
+ export interface SmsMessageListParams {
1981
+ readonly conversation_id?: string;
1982
+ readonly batch_id?: string;
1983
+ readonly from_phone_number_id?: string;
1984
+ readonly recipient?: string;
1985
+ readonly campaign_id?: string;
1986
+ readonly status?: SmsMessageStatus;
1987
+ readonly direction?: SmsMessageDirection;
1988
+ readonly origin?: SmsMessageOrigin;
1989
+ readonly created_after?: string;
1990
+ readonly created_before?: string;
1991
+ readonly cursor?: string;
1992
+ readonly limit?: number;
1993
+ }
1994
+ export interface SmsBatchRecipient {
1995
+ readonly to: string;
1996
+ readonly text: string;
1997
+ readonly consent_id?: string | null;
1998
+ readonly recipient_timezone?: string | null;
1999
+ readonly metadata?: Record<string, unknown>;
2000
+ }
2001
+ export interface SmsBatchCreateParams {
2002
+ readonly from_phone_number_id: string;
2003
+ readonly recipients: readonly SmsBatchRecipient[];
2004
+ readonly idempotencyKey: string;
2005
+ readonly send_at?: string;
2006
+ }
2007
+ export interface SmsBatch {
2008
+ readonly id: string;
2009
+ readonly from_phone_number_id: string;
2010
+ readonly status: 'queued' | 'scheduled' | 'processing' | 'completed' | 'partially_failed' | 'failed' | 'canceled';
2011
+ readonly requested_send_at: string | null;
2012
+ readonly total_count: number;
2013
+ readonly accepted_count: number;
2014
+ readonly rejected_count: number;
2015
+ readonly delivered_count: number;
2016
+ readonly failed_count: number;
2017
+ readonly canceled_at: string | null;
2018
+ readonly completed_at: string | null;
2019
+ readonly created_at: string;
2020
+ readonly updated_at: string;
2021
+ }
2022
+ export interface SmsConversation {
2023
+ readonly id: string;
2024
+ readonly phone_number_id: string;
2025
+ readonly remote_phone_number: string;
2026
+ readonly campaign_id: string | null;
2027
+ readonly campaign_snapshot: Record<string, unknown> | null;
2028
+ readonly status: 'open' | 'closed' | 'spam';
2029
+ readonly automation_status: 'disabled' | 'enabled' | 'paused';
2030
+ readonly assigned_user_id: string | null;
2031
+ readonly assigned_agent_id: string | null;
2032
+ readonly unread_count: number;
2033
+ readonly recipient_timezone: string | null;
2034
+ readonly last_inbound_at: string | null;
2035
+ readonly last_outbound_at: string | null;
2036
+ readonly last_message_at: string;
2037
+ readonly content_redacted_at: string | null;
2038
+ readonly created_at: string;
2039
+ readonly updated_at: string;
2040
+ }
2041
+ export interface SmsConversationListParams {
2042
+ readonly status?: SmsConversation['status'];
2043
+ readonly phone_number_id?: string;
2044
+ readonly assigned_user_id?: string;
2045
+ readonly assigned_agent_id?: string;
2046
+ readonly recipient?: string;
2047
+ readonly unread?: boolean;
2048
+ readonly cursor?: string;
2049
+ readonly limit?: number;
2050
+ }
2051
+ export interface SmsConversationUpdate {
2052
+ readonly status?: SmsConversation['status'];
2053
+ readonly assigned_user_id?: string | null;
2054
+ readonly assigned_agent_id?: string | null;
2055
+ readonly automation_status?: SmsConversation['automation_status'];
2056
+ readonly recipient_timezone?: string | null;
2057
+ }
2058
+ export interface SmsConversationSendParams {
2059
+ readonly text: string;
2060
+ readonly idempotencyKey: string;
2061
+ readonly send_at?: string;
2062
+ readonly consent_id?: string | null;
2063
+ readonly recipient_timezone?: string | null;
2064
+ readonly metadata?: Record<string, unknown>;
2065
+ }
2066
+ export type SmsConsentSource = 'inbound' | 'api' | 'keyword' | 'webform' | 'paper' | 'verbal' | 'import';
2067
+ export interface SmsConsentInput {
2068
+ readonly recipient: string;
2069
+ readonly campaign_id: string;
2070
+ readonly source: SmsConsentSource;
2071
+ readonly proof_reference?: string | null;
2072
+ readonly proof?: string | null;
2073
+ readonly timezone?: string | null;
2074
+ readonly captured_at?: string;
2075
+ readonly expires_at?: string | null;
2076
+ readonly metadata?: Record<string, unknown>;
2077
+ }
2078
+ export interface SmsConsent {
2079
+ readonly id: string;
2080
+ readonly recipient: string;
2081
+ readonly campaign_id: string;
2082
+ readonly status: 'active' | 'expired' | 'revoked';
2083
+ readonly source: SmsConsentSource;
2084
+ readonly proof_reference: string | null;
2085
+ readonly proof_hash: string | null;
2086
+ readonly timezone: string | null;
2087
+ readonly captured_at: string;
2088
+ readonly expires_at: string | null;
2089
+ readonly revoked_at: string | null;
2090
+ readonly revoked_reason: string | null;
2091
+ readonly metadata: Record<string, unknown>;
2092
+ readonly created_at: string;
2093
+ }
2094
+ export interface SmsConsentListParams {
2095
+ readonly recipient?: string;
2096
+ readonly campaign_id?: string;
2097
+ readonly status?: SmsConsent['status'];
2098
+ readonly limit?: number;
2099
+ }
2100
+ export interface SmsSuppression {
2101
+ readonly id: string;
2102
+ readonly recipient: string;
2103
+ readonly status: 'suppressed' | 'lifted';
2104
+ readonly keyword: string | null;
2105
+ readonly source_phone_number_id: string | null;
2106
+ readonly source_message_provider_id: string | null;
2107
+ readonly suppressed_at: string;
2108
+ readonly lifted_at: string | null;
2109
+ readonly updated_at: string;
2110
+ }
2111
+ export interface SmsSettings {
2112
+ readonly messaging_profile_id: string | null;
2113
+ readonly messaging_profile_status: string;
2114
+ readonly webhook_config_version: number;
2115
+ readonly opt_out_config_version: number;
2116
+ readonly help_message: string;
2117
+ readonly opt_out_message: string;
2118
+ readonly opt_in_message: string;
2119
+ readonly retention_days: number;
2120
+ readonly quiet_hours_start: string;
2121
+ readonly quiet_hours_end: string;
2122
+ readonly default_timezone: string | null;
2123
+ readonly default_automation_enabled: boolean;
2124
+ readonly last_synced_at: string | null;
2125
+ readonly last_error: string | null;
2126
+ readonly updated_at: string;
2127
+ }
2128
+ export type SmsSettingsUpdate = Partial<Pick<SmsSettings, 'help_message' | 'opt_out_message' | 'opt_in_message' | 'retention_days' | 'quiet_hours_start' | 'quiet_hours_end' | 'default_timezone' | 'default_automation_enabled'>>;
2129
+ export interface SmsConversationNote {
2130
+ readonly id: string;
2131
+ readonly conversation_id: string;
2132
+ readonly body: string | null;
2133
+ readonly created_by_user_id: string;
2134
+ readonly redacted_at: string | null;
2135
+ readonly created_at: string;
2136
+ }
2137
+ export interface SmsStreamEvent {
2138
+ readonly event: string;
2139
+ readonly id?: string;
2140
+ readonly message_id: string;
2141
+ readonly conversation_id: string;
2142
+ readonly status: SmsMessageStatus;
2143
+ readonly occurred_at: string;
2144
+ }
1156
2145
  //# sourceMappingURL=index.d.ts.map