feat: support inbound vonage calls (#480)

* feat: support inbound vonage calls

* fix: drift check

* feat: add warning with missing signature secret

* docs: vonage inbound steps

* chore: upgrade pipecat submodule
This commit is contained in:
Sabiha Khan 2026-06-29 16:27:19 +05:30 committed by GitHub
parent f190a0dd9a
commit d9800fddd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 687 additions and 83 deletions

4
ui/package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "ui",
"version": "1.35.0",
"version": "1.39.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ui",
"version": "1.35.0",
"version": "1.39.0",
"dependencies": {
"@dagrejs/dagre": "^1.1.4",
"@radix-ui/react-alert-dialog": "^1.1.15",

View file

@ -53,6 +53,7 @@ export default function TelephonyConfigurationsPage() {
const { user, getAccessToken, loading: authLoading } = useAuth();
const {
telnyxMissingWebhookPublicKeyCount,
vonageMissingSignatureSecretCount,
refresh: refreshWarnings,
} = useTelephonyConfigWarnings();
const [items, setItems] = useState<TelephonyConfigurationListItem[]>([]);
@ -82,9 +83,9 @@ export default function TelephonyConfigurationsPage() {
}
}, [authLoading, user, getAccessToken]);
// After a save (create/update), the backing config may have flipped between
// missing/present webhook_public_key — refresh the cached warning state so
// the page banner and nav badge update without a manual reload.
// After a save (create/update), webhook-verification warning state may have
// changed — refresh the cached warning state so the page banner and nav badge
// update without a manual reload.
const onSaved = useCallback(async () => {
await fetchItems();
await refreshWarnings();
@ -194,6 +195,26 @@ export default function TelephonyConfigurationsPage() {
</div>
)}
{vonageMissingSignatureSecretCount > 0 && (
<div className="mb-6 rounded-md border border-amber-300 bg-amber-50 p-4 text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-200">
<div className="flex items-start gap-3">
<AlertTriangle className="h-5 w-5 shrink-0 mt-0.5" />
<div className="space-y-1 text-sm">
<p className="font-medium">Signature secret not configured</p>
<p>
{vonageMissingSignatureSecretCount === 1
? "1 Vonage configuration is"
: `${vonageMissingSignatureSecretCount} Vonage configurations are`}{" "}
missing a signature secret. Without it, Vonage signed webhooks
are rejected, so inbound calls and call status updates will not
work. Copy the signature secret from your Vonage account and
paste it into the affected Vonage configuration below.
</p>
</div>
</div>
</div>
)}
{loading ? (
<div className="grid gap-3">
<Skeleton className="h-24 w-full" />

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -5323,6 +5323,10 @@ export type TelephonyConfigWarningsResponse = {
* Telnyx Missing Webhook Public Key Count
*/
telnyx_missing_webhook_public_key_count: number;
/**
* Vonage Missing Signature Secret Count
*/
vonage_missing_signature_secret_count: number;
};
/**
@ -6427,6 +6431,12 @@ export type VonageConfigurationRequest = {
* Private key for JWT generation
*/
private_key: string;
/**
* Signature Secret
*
* Vonage signature secret used to verify signed webhooks
*/
signature_secret?: string | null;
/**
* From Numbers
*
@ -6461,6 +6471,10 @@ export type VonageConfigurationResponse = {
* Private Key
*/
private_key: string;
/**
* Signature Secret
*/
signature_secret?: string | null;
/**
* From Numbers
*/
@ -7553,6 +7567,27 @@ export type HandleVonageEventsApiV1TelephonyVonageEventsWorkflowRunIdPostRespons
200: unknown;
};
export type HandleVonageEventsWithoutRunApiV1TelephonyVonageEventsPostData = {
body?: never;
path?: never;
query?: never;
url: '/api/v1/telephony/vonage/events';
};
export type HandleVonageEventsWithoutRunApiV1TelephonyVonageEventsPostErrors = {
/**
* Not found
*/
404: unknown;
};
export type HandleVonageEventsWithoutRunApiV1TelephonyVonageEventsPostResponses = {
/**
* Successful Response
*/
200: unknown;
};
export type ImpersonateApiV1SuperuserImpersonatePostData = {
body: ImpersonateRequest;
headers?: {

View file

@ -167,8 +167,13 @@ export function AppSidebar() {
const { provider, getSelectedTeam, logout, user } = useAuth();
const { config } = useAppConfig();
const { openHireExpert } = useLeadForms();
const { telnyxMissingWebhookPublicKeyCount } = useTelephonyConfigWarnings();
const hasTelephonyWarning = telnyxMissingWebhookPublicKeyCount > 0;
const {
telnyxMissingWebhookPublicKeyCount,
vonageMissingSignatureSecretCount,
} = useTelephonyConfigWarnings();
const hasTelephonyWarning =
telnyxMissingWebhookPublicKeyCount > 0 ||
vonageMissingSignatureSecretCount > 0;
const isCollapsed = !isMobile && state === "collapsed";
// Get selected team for Stack auth (cast to Team type from Stack)

View file

@ -7,12 +7,14 @@ import { useAuth } from '@/lib/auth';
interface TelephonyConfigWarningsContextType {
telnyxMissingWebhookPublicKeyCount: number;
vonageMissingSignatureSecretCount: number;
refresh: () => Promise<void>;
loading: boolean;
}
const TelephonyConfigWarningsContext = createContext<TelephonyConfigWarningsContextType>({
telnyxMissingWebhookPublicKeyCount: 0,
vonageMissingSignatureSecretCount: 0,
refresh: async () => { },
loading: false,
});
@ -23,7 +25,8 @@ const TelephonyConfigWarningsContext = createContext<TelephonyConfigWarningsCont
// change. Page-level callers invalidate via refresh() after a save.
export function TelephonyConfigWarningsProvider({ children }: { children: ReactNode }) {
const auth = useAuth();
const [count, setCount] = useState(0);
const [telnyxCount, setTelnyxCount] = useState(0);
const [vonageCount, setVonageCount] = useState(0);
const [loading, setLoading] = useState(false);
const hasFetched = useRef(false);
@ -31,9 +34,11 @@ export function TelephonyConfigWarningsProvider({ children }: { children: ReactN
setLoading(true);
try {
const res = await getTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGet();
setCount(res.data?.telnyx_missing_webhook_public_key_count ?? 0);
setTelnyxCount(res.data?.telnyx_missing_webhook_public_key_count ?? 0);
setVonageCount(res.data?.vonage_missing_signature_secret_count ?? 0);
} catch {
setCount(0);
setTelnyxCount(0);
setVonageCount(0);
} finally {
setLoading(false);
}
@ -53,7 +58,8 @@ export function TelephonyConfigWarningsProvider({ children }: { children: ReactN
return (
<TelephonyConfigWarningsContext.Provider
value={{
telnyxMissingWebhookPublicKeyCount: count,
telnyxMissingWebhookPublicKeyCount: telnyxCount,
vonageMissingSignatureSecretCount: vonageCount,
refresh,
loading,
}}