Skip to content

Feat/vobiz telephony#141

Open
Piyush-sahoo wants to merge 8 commits into
rapidaai:mainfrom
Piyush-sahoo:feat/vobiz-telephony
Open

Feat/vobiz telephony#141
Piyush-sahoo wants to merge 8 commits into
rapidaai:mainfrom
Piyush-sahoo:feat/vobiz-telephony

Conversation

@Piyush-sahoo

@Piyush-sahoo Piyush-sahoo commented Jun 11, 2026

Copy link
Copy Markdown

Description

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Configuration change
  • Refactoring (no functional changes)
  • Test update
  • Security fix

Related Issues

Fixes #

Checklist

General

  • I have read the CONTRIBUTING guidelines
  • My code follows the project's coding standards
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas

Testing

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have tested my changes on multiple platforms (if applicable)

Documentation

  • I have updated the documentation accordingly
  • I have updated the API documentation (if applicable)

Security

  • My changes do not introduce any security vulnerabilities
  • I have not committed any sensitive data (API keys, passwords, etc.)

Screenshots (if applicable)

Additional Notes

Summary by CodeRabbit

  • New Features

    • Vobiz telephony support (SIP and WebSocket) including UI provider entries, caller/auth settings, and telephony answer XML endpoints.
    • Automatic Vobiz SIP trunk provisioning and inbound DID provisioning during credential creation/deployment.
    • New WebSocket streamer and audio processing for Vobiz media.
  • Bug Fixes / Behavior

    • SIP routing/registration expanded to include Vobiz SIP variants.
  • Tests

    • Added unit tests for Vobiz client API.

Add Vobiz as a telephony provider with two transports: WebSocket media streaming (vobiz_websocket) and auto-provisioned SIP trunking (vobiz_sip) reusing the existing SIP engine. Includes the Vobiz REST client (trunk + standalone credential provisioning with credential_uuid attach), vault-backed credential storage, inbound/outbound call handling, answer-XML Stream generation, status-callback parsing, and mulaw/L16 audio processing. Wires both providers into the telephony and streamer factories, widens SIP selection and the answer_url route to cover vobiz_sip, and adds UI provider catalog entries.
Embed the Vobiz brand mark (inline SVG data URI) as the icon for the vobiz_sip and vobiz_websocket provider cards, replacing the generic SIP icon.
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Vobiz telephony support (WebSocket + auto-provisioned SIP): REST client, provider implementations, audio resampling/processor, websocket streamer, SIP provisioning and vault integration, router and UI updates, and direct vault helper.

Changes

Vobiz Telephony Provider Implementation

Layer / File(s) Summary
Vobiz REST Client Foundation
pkg/clients/vobiz/types.go, pkg/clients/vobiz/client.go, pkg/clients/vobiz/client_test.go
REST client interface and implementation for trunk/credential/origination/call operations with per-request auth headers, non-2xx error mapping, response validation, and unit tests.
Vobiz WebSocket Event Schema and Status Callback
api/assistant-api/internal/channel/telephony/internal/vobiz/internal/types.go, api/assistant-api/internal/channel/telephony/internal/vobiz/internal/status_callback.go
WebSocket event/message types, outbound audio format and framing constants for μ-law 8kHz ↔ linear16 16kHz bridging, and status callback parsing with completion/failure detection.
Vobiz Telephony Provider
api/assistant-api/internal/channel/telephony/internal/vobiz/telephony.go
Core provider implementation: vault credential extraction, AnswerXML generation, OutboundCall/InboundCall flows, webhook parsing, and status callback endpoints.
Audio Codec Conversion and Resampling
api/assistant-api/internal/channel/telephony/internal/vobiz/internal/audio_processor.go
AudioProcessor performing resampling between provider μ-law 8kHz and engine linear16 16kHz, buffering, ambient mixing, silence frames, and output health tracking.
Vobiz WebSocket Streamer
api/assistant-api/internal/channel/telephony/internal/vobiz/websocket.go
WebSocket streamer bridging Vobiz media/control events to internal MediaSession: reader goroutine, media handling, outgoing play-audio/control events, interruption/disconnect handling, and shutdown.
Telephony Provider Registration and Routing
api/assistant-api/internal/channel/telephony/telephony.go, api/assistant-api/internal/channel/telephony/streamer.go, api/assistant-api/internal/channel/telephony/outbound.go, api/assistant-api/sip/registration/record.go, api/assistant-api/sip/sip.go
Adds vobiz_websocket and vobiz_sip providers, factory routing to Vobiz implementations, extends SIP timeout handling to VobizSIP, and broadens SIP/DID filters to include vobiz_sip.
Vobiz SIP Trunk Auto-Provisioning
api/web-api/internal/service/vault/vobiz.go, api/web-api/internal/service/vault/service.go
Provisioning flow: validate inputs, optional password generation, idempotent trunk lookup, Vobiz credential/trunk creation with rollback on failure, and persist combined SIP+Vobiz metadata to vault.
Assistant Phone Deployment Inbound Provisioning
api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go, api/assistant-api/api/assistant-deployment/create_assistant_phone_deployment.go
Integrates inbound provisioning into phone deployment creation, idempotency checks, error mapping via InboundProvisioningError, and surfaces user-safe messages on failure.
HTTP API Handler and Router Wiring
api/assistant-api/api/talk/answer.go, api/assistant-api/router/assistant.go
Adds AnswerXML HTTP handler for context-based callbacks and registers GET/POST /v1/talk/:telephony/ctx/:contextId/answer routes; deployment route updated to pass Redis connector.
UI Provider Configuration and Credential Modal
ui/src/providers/provider.development.json, ui/src/providers/provider.production.json, ui/src/providers/vobiz_sip/telephony.json, ui/src/providers/vobiz_websocket/telephony.json, ui/src/app/components/base/modal/create-provider-credential-modal/index.tsx
Adds provider registry entries for Vobiz SIP and WebSocket, telephony parameter schemas, treats sip_password as optional for vobiz_sip, and adjusts success toast messaging.
Direct Vault Credential gRPC Helper
pkg/clients/web/vault_client.go
Adds GetCredentialDirect helper to fetch a vault credential via direct gRPC call using a service-scope token and outgoing metadata.

Sequence Diagram(s)

sequenceDiagram
  participant Client as API Client
  participant VobizProvider
  participant VobizClient
  participant VobizAPI as Vobiz API
  Client->>VobizProvider: OutboundCall(toPhone, fromPhone, assistant, context_id)
  VobizProvider->>VobizProvider: validate creds, build answerURL/callbacks
  VobizProvider->>VobizClient: MakeCall(authID, authToken, MakeCallRequest)
  VobizClient->>VobizAPI: POST /calls (auth headers)
  VobizAPI-->>VobizClient: 2xx {RequestUUID}
  VobizClient-->>VobizProvider: CallResponse
  VobizProvider->>Client: CallInfo (with RequestUUID)
Loading
sequenceDiagram
  participant WebSocket as Vobiz WebSocket
  participant Streamer
  participant AudioProcessor
  participant MediaSession as Internal Media
  WebSocket->>Streamer: VobizMediaEvent {EventType: start, streamID}
  Streamer->>MediaSession: Start()
  WebSocket->>Streamer: VobizMediaEvent {EventType: media, payload}
  Streamer->>AudioProcessor: ProcessProviderAudioFrame(mulaw 8kHz)
  AudioProcessor->>MediaSession: InputAudioFrame (linear16 16kHz)
  MediaSession->>AudioProcessor: ProcessAssistantAudio(linear16 16kHz)
  AudioProcessor->>Streamer: NextOutputFrame (mulaw 8kHz)
  Streamer->>WebSocket: sendPlayAudio(VobizPlayAudioMessage)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

feature, enhancement

🐰 I hopped through code to bridge the calls,
mixing μ-law with PCM in tidy halls.
Vobiz trunks and webs now sing,
streams and provisioning take wing,
Rabbit cheers — connections answered all!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat/vobiz telephony' is directly related to the main change, which adds comprehensive Vobiz telephony provider support across multiple modules.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Piyush-sahoo Piyush-sahoo force-pushed the feat/vobiz-telephony branch from e7c5124 to 3aa77ba Compare June 11, 2026 15:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (10)
api/assistant-api/sip/sip.go (2)

580-580: ⚡ Quick win

Consider using Telephony constants instead of string literals.

The query uses []string{"sip", "vobiz_sip"} as string literals, but Telephony constants are defined in api/assistant-api/internal/channel/telephony/telephony.go. Using []string{channel_telephony.SIP.String(), channel_telephony.VobizSIP.String()} would improve maintainability and prevent silent breakage if constant values change.

♻️ Refactor to use constants
-	Where("apd.telephony_provider IN ? AND apd.status = ?", []string{"sip", "vobiz_sip"}, type_enums.RECORD_ACTIVE).
+	Where("apd.telephony_provider IN ? AND apd.status = ?", []string{channel_telephony.SIP.String(), channel_telephony.VobizSIP.String()}, type_enums.RECORD_ACTIVE).

You'll need to add the import:

channel_telephony "github.com/rapidaai/api/assistant-api/internal/channel/telephony"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/assistant-api/sip/sip.go` at line 580, Replace the string literals in the
DB filter with the Telephony constants: change the slice []string{"sip",
"vobiz_sip"} to use channel_telephony.SIP.String() and
channel_telephony.VobizSIP.String() so the query in the Where(...) call uses the
defined constants; also add the import alias channel_telephony
"github.com/rapidaai/api/assistant-api/internal/channel/telephony" at the top of
the file so the symbols resolve.

81-83: ⚡ Quick win

Consider constructing VaultClient once and reusing the instance.

NewVaultClientGRPC is called twice with identical arguments — once for the deployment service (line 81) and once for the engine's own field (line 83). Constructing the client once and passing the same instance to both would avoid duplicate initialization and potential duplicate gRPC connections.

♻️ Refactor to construct once
+	vaultClient := web_client.NewVaultClientGRPC(&config.AppConfig, logger, redis)
 	return &SIPEngine{
 		cfg:                          config,
 		logger:                       logger,
 		postgres:                     postgres,
 		redis:                        redis,
 		opensearch:                   opensearch,
 		assistantConversationService: internal_assistant_service.NewAssistantConversationService(logger, postgres, storage_files.NewStorage(config.AssetStoreConfig, logger)),
 		assistantService:             internal_assistant_service.NewAssistantService(config, logger, postgres, opensearch),
-		deploymentService:            internal_assistant_service.NewAssistantDeploymentService(config, logger, postgres, web_client.NewVaultClientGRPC(&config.AppConfig, logger, redis)),
+		deploymentService:            internal_assistant_service.NewAssistantDeploymentService(config, logger, postgres, vaultClient),
 		storage:                      storage_files.NewStorage(config.AssetStoreConfig, logger),
-		vaultClient:                  web_client.NewVaultClientGRPC(&config.AppConfig, logger, redis),
+		vaultClient:                  vaultClient,
 		callContextStore:             callcontext.NewStore(postgres, logger),
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/assistant-api/sip/sip.go` around lines 81 - 83, The code calls
web_client.NewVaultClientGRPC(&config.AppConfig, logger, redis) twice; create
the Vault client once and reuse it by instantiating a single variable (e.g.,
vaultClient := web_client.NewVaultClientGRPC(&config.AppConfig, logger, redis))
and pass that same instance into
internal_assistant_service.NewAssistantDeploymentService(...) for the
deploymentService parameter and assign it to the engine's vaultClient field,
updating uses of deploymentService and vaultClient accordingly to avoid
duplicate initialization and gRPC connections.
api/assistant-api/sip/registration/record.go (1)

35-35: ⚡ Quick win

Consider using Telephony constants instead of string literals.

The query uses []string{"sip", "vobiz_sip"} as string literals, but Telephony constants are defined in api/assistant-api/internal/channel/telephony/telephony.go. Using []string{channel_telephony.SIP.String(), channel_telephony.VobizSIP.String()} would improve maintainability and prevent silent breakage if constant values change.

♻️ Refactor to use constants
-	Where("telephony_provider IN ? AND status = ?", []string{"sip", "vobiz_sip"}, type_enums.RECORD_ACTIVE).
+	Where("telephony_provider IN ? AND status = ?", []string{channel_telephony.SIP.String(), channel_telephony.VobizSIP.String()}, type_enums.RECORD_ACTIVE).

You'll need to add the import:

channel_telephony "github.com/rapidaai/api/assistant-api/internal/channel/telephony"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/assistant-api/sip/registration/record.go` at line 35, Replace the string
literals []string{"sip", "vobiz_sip"} used in the GORM Where clause with the
Telephony constants (e.g. channel_telephony.SIP.String() and
channel_telephony.VobizSIP.String()) so the query becomes
Where("telephony_provider IN ? AND status = ?",
[]string{channel_telephony.SIP.String(), channel_telephony.VobizSIP.String()},
type_enums.RECORD_ACTIVE); add the import alias channel_telephony from the
internal/channel/telephony package and ensure the code compiles and tests pass
after the change.
api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go (7)

446-449: 💤 Low value

Use fmt.Errorf instead of errors.New for consistency.

As per coding guidelines, Go errors should use fmt.Errorf for consistent formatting and to enable context wrapping when needed.

🔧 Proposed fix
 	if credIDStr == "" {
-		return nil, errors.New("a Vobiz SIP credential is required to enable inbound calls")
+		return nil, fmt.Errorf("a Vobiz SIP credential is required to enable inbound calls")
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 446 - 449, Replace the errors.New call with fmt.Errorf for
consistency and potential wrapping: when reading optMap["rapida.credential_id"]
into credIDStr and returning the error "a Vobiz SIP credential is required to
enable inbound calls", use fmt.Errorf to construct the error (and add fmt to the
imports if missing); keep the same message but use fmt.Errorf to follow project
error formatting conventions.

Source: Coding guidelines


465-467: 💤 Low value

Use fmt.Errorf instead of errors.New for consistency.

As per coding guidelines, Go errors should use fmt.Errorf for consistent formatting and to enable context wrapping when needed.

🔧 Proposed fix
 	if eService.cfg.SIPConfig == nil || eService.cfg.SIPConfig.ExternalIP == "" {
-		return nil, errors.New("the assistant SIP server has no external IP configured; cannot provision inbound")
+		return nil, fmt.Errorf("the assistant SIP server has no external IP configured; cannot provision inbound")
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 465 - 467, Replace the errors.New call with fmt.Errorf to follow
project conventions: in the block that checks eService.cfg.SIPConfig and
eService.cfg.SIPConfig.ExternalIP (the code returning "return nil,
errors.New(...)"), change it to return nil, fmt.Errorf("the assistant SIP server
has no external IP configured; cannot provision inbound") and ensure the "fmt"
package is imported; keep the message intact so it can be wrapped later if
needed.

Source: Coding guidelines


454-456: 💤 Low value

Use fmt.Errorf instead of errors.New for consistency.

As per coding guidelines, Go errors should use fmt.Errorf for consistent formatting and to enable context wrapping when needed.

🔧 Proposed fix
 	if eService.vaultClient == nil {
-		return nil, errors.New("inbound provisioning is unavailable in this context")
+		return nil, fmt.Errorf("inbound provisioning is unavailable in this context")
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 454 - 456, The code returns errors.New(...) when
eService.vaultClient is nil; replace that with fmt.Errorf("inbound provisioning
is unavailable in this context") to follow project convention and enable future
error wrapping, and add an import for "fmt" in the file if not already present;
update the return in the method where eService.vaultClient is checked (the block
starting with "if eService.vaultClient == nil") to use fmt.Errorf.

Source: Coding guidelines


476-478: 💤 Low value

Use fmt.Errorf instead of errors.New for consistency.

As per coding guidelines, Go errors should use fmt.Errorf for consistent formatting and to enable context wrapping when needed.

🔧 Proposed fix
 	if authID == "" || authToken == "" {
-		return nil, errors.New("the Vobiz SIP credential is missing auth_id/auth_token")
+		return nil, fmt.Errorf("the Vobiz SIP credential is missing auth_id/auth_token")
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 476 - 478, Replace the errors.New call with fmt.Errorf to follow
project error formatting conventions: change `return nil, errors.New("the Vobiz
SIP credential is missing auth_id/auth_token")` to `return nil, fmt.Errorf("the
Vobiz SIP credential is missing auth_id/auth_token")`, and add the fmt import if
not present; keep the same message so callers' behavior is unchanged but now
supports wrapping/formatting for variables like authID/authToken.

Source: Coding guidelines


469-472: 💤 Low value

Use fmt.Errorf instead of errors.New for consistency.

As per coding guidelines, Go errors should use fmt.Errorf for consistent formatting and to enable context wrapping when needed.

🔧 Proposed fix
 	if err != nil {
-		return nil, errors.New("could not read the Vobiz SIP credential")
+		return nil, fmt.Errorf("could not read the Vobiz SIP credential: %w", err)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 469 - 472, Replace the plain errors.New usage in the GetCredential
error branch with fmt.Errorf so the original error is wrapped and preserved;
specifically, in the block after calling eService.vaultClient.GetCredential(ctx,
auth, credentialID) (variables cred, err, credentialID), return
fmt.Errorf("could not read the Vobiz SIP credential: %w", err) instead of
errors.New(...), and add an import for "fmt" if missing.

Source: Coding guidelines


450-453: 💤 Low value

Use fmt.Errorf instead of errors.New for consistency.

As per coding guidelines, Go errors should use fmt.Errorf for consistent formatting and to enable context wrapping when needed.

🔧 Proposed fix
 	if err != nil {
-		return nil, errors.New("invalid Vobiz SIP credential")
+		return nil, fmt.Errorf("invalid Vobiz SIP credential")
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 450 - 453, Replace the errors.New call when parsing the SIP
credential ID with fmt.Errorf and wrap the original parse error for context:
after strconv.ParseUint(credIDStr, 10, 64) returns err, return
fmt.Errorf("invalid Vobiz SIP credential: %w", err) (referencing credIDStr,
credentialID and the ParseUint call) so the original error is preserved and
formatting is consistent.

Source: Coding guidelines


443-445: 💤 Low value

Use fmt.Errorf instead of errors.New for consistency.

As per coding guidelines, Go errors should use fmt.Errorf for consistent formatting and to enable context wrapping when needed.

🔧 Proposed fix
 	if did == "" {
-		return nil, errors.New("a caller-ID phone number is required to enable inbound calls")
+		return nil, fmt.Errorf("a caller-ID phone number is required to enable inbound calls")
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 443 - 445, Replace the errors.New call with fmt.Errorf to follow
the project's error formatting convention: change return nil, errors.New("a
caller-ID phone number is required to enable inbound calls") to return nil,
fmt.Errorf("a caller-ID phone number is required to enable inbound calls") and
ensure the fmt package is imported (or add it to the import list if missing) in
assistant.deployment.impl.service.go; the affected check uses the did variable
that validates caller ID.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/assistant-api/api/talk/answer.go`:
- Around line 44-46: The log message hard-codes "vobiz" even though this handler
is provider-agnostic; update the error log to reference the path param/provider
variable instead of "vobiz". Locate the handler where cApi.logger.Errorf is
called (the answer handler that reads the telephony provider from the request,
e.g., via c.Param("provider") or a local variable named provider) and change the
message to include that provider (for example: use provider variable in the
format string like "%s answer XML build failed: %v") so the log is generic and
reflects the actual provider.

In `@api/web-api/internal/service/vault/service.go`:
- Around line 38-40: The branch that directly returns vs.provisionVobiz(ctx,
auth, name, credential) should capture the error and wrap it with fmt.Errorf
using the %w verb to preserve the chain; replace the direct return with e.g. err
:= vs.provisionVobiz(ctx, auth, name, credential); if err != nil { return
fmt.Errorf("provisionVobiz failed: %w", err) } and ensure fmt is imported. Keep
the wrapped message concise and reference provisionVobiz/ vs.provisionVobiz so
the source of the failure is clear.

In `@api/web-api/internal/service/vault/vobiz.go`:
- Around line 176-178: The call to rand.Read(b) in the function in vobiz.go
returns the raw error; update the error return to wrap it with context using
fmt.Errorf and %w (for example: return "", fmt.Errorf("reading random bytes:
%w", err)) so the cryptographic error from rand.Read is preserved with
contextual information; import fmt if not already present and keep the existing
return types intact.
- Around line 139-141: The DB query error returned from the call that ends with
").Find(&rows).Error" should be wrapped with context using fmt.Errorf and %w
before returning; change the error return in that conditional to something like:
return nil, fmt.Errorf("querying vault vobiz rows: %w", err) (use an
appropriate, concise message referencing the operation) so the original error
chain is preserved.
- Around line 55-57: The validation check for sipUsername currently returns a
plain fmt.Errorf string; create a sentinel error (e.g. ErrMissingSIPUsername)
and return a wrapped error using fmt.Errorf with %w so callers can unwrap it.
Update the check in vobiz.go (the sipUsername validation) to return
fmt.Errorf("vobiz sip_username is required: %w", ErrMissingSIPUsername) instead
of the bare message, and declare ErrMissingSIPUsername = errors.New("missing
sip_username") near the top of the file.
- Around line 52-54: The validation branch that checks authID/authToken should
return a wrapped, contextual error instead of a plain fmt.Errorf string; create
a sentinel error (e.g., validationErr := errors.New("auth_id and auth_token are
required")) and return fmt.Errorf("vobiz validation: %w", validationErr) from
the authID/authToken check in the function that contains the authID/authToken
validation so the error is consistently wrapped with context (refer to the
authID/authToken validation block in vobiz.go).
- Around line 64-66: The validation return for sipPassword should wrap a
sentinel error for consistency: replace the bare fmt.Errorf(...) return in the
sipPassword length check with a wrapped error using fmt.Errorf("vobiz
sip_password must be at least 8 characters: %w", ErrInvalidParameter) (or create
a package-level sentinel like ErrInvalidParameter/ErrInvalidConfig if none
exists) so callers can inspect the underlying error while preserving the
descriptive message; update the sipPassword length check in vobiz.go
accordingly.

In `@pkg/clients/vobiz/client.go`:
- Around line 67-84: The validation error returned when trunk.TrunkID or
trunk.TrunkDomain is missing in CreateTrunk should be wrapped with a %w-wrapped
sentinel error to preserve error chains; define a package-level error (e.g.,
ErrInvalidTrunkResponse) and change the failing return in CreateTrunk (the
fmt.Errorf that includes resp.ToString()) to wrap that sentinel using "%w"
(e.g., fmt.Errorf("vobiz trunk response missing trunk_id/trunk_domain: %s: %w",
resp.ToString(), ErrInvalidTrunkResponse)). This keeps the existing
message/context while enabling callers to inspect the underlying validation
error.

In `@ui/src/app/components/base/modal/create-provider-credential-modal/index.tsx`:
- Around line 120-122: The success message check in
create-provider-credential-modal (index.tsx) uses provider.code === 'vobiz' but
the actual provider code is 'vobiz_sip', so the specialized Vobiz message never
appears; update the condition that reads provider.code === 'vobiz' to match the
real identifier (e.g., provider.code === 'vobiz_sip') or use a more flexible
check like provider.code?.startsWith('vobiz') so the Vobiz-specific success
message is shown for the real provider code.
- Around line 73-74: The conditional that makes the sip_password optional checks
provider?.code === 'vobiz' but the actual provider code is 'vobiz_sip'; update
the check in create-provider-credential-modal (the if handling field.name ===
'sip_password') to match the real provider code (e.g., change 'vobiz' to
'vobiz_sip' or compare against a canonical provider constant), so the branch
returns false for the correct provider code.

In `@ui/src/providers/vobiz_websocket/telephony.json`:
- Line 10: Update the placeholder value in the telephony.json entry that
currently uses "placeholder": "+91..." to a neutral, consistent example (e.g.,
an E.164 example like "e.g., +15551234567" or a generic "+...") so it matches
other Vobiz provider files (see the "placeholder" key in
vobiz_websocket/telephony.json and compare with vobiz_sip/telephony.json);
replace the regional "+91..." string with the chosen neutral placeholder in the
"placeholder" field.

---

Nitpick comments:
In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`:
- Around line 446-449: Replace the errors.New call with fmt.Errorf for
consistency and potential wrapping: when reading optMap["rapida.credential_id"]
into credIDStr and returning the error "a Vobiz SIP credential is required to
enable inbound calls", use fmt.Errorf to construct the error (and add fmt to the
imports if missing); keep the same message but use fmt.Errorf to follow project
error formatting conventions.
- Around line 465-467: Replace the errors.New call with fmt.Errorf to follow
project conventions: in the block that checks eService.cfg.SIPConfig and
eService.cfg.SIPConfig.ExternalIP (the code returning "return nil,
errors.New(...)"), change it to return nil, fmt.Errorf("the assistant SIP server
has no external IP configured; cannot provision inbound") and ensure the "fmt"
package is imported; keep the message intact so it can be wrapped later if
needed.
- Around line 454-456: The code returns errors.New(...) when
eService.vaultClient is nil; replace that with fmt.Errorf("inbound provisioning
is unavailable in this context") to follow project convention and enable future
error wrapping, and add an import for "fmt" in the file if not already present;
update the return in the method where eService.vaultClient is checked (the block
starting with "if eService.vaultClient == nil") to use fmt.Errorf.
- Around line 476-478: Replace the errors.New call with fmt.Errorf to follow
project error formatting conventions: change `return nil, errors.New("the Vobiz
SIP credential is missing auth_id/auth_token")` to `return nil, fmt.Errorf("the
Vobiz SIP credential is missing auth_id/auth_token")`, and add the fmt import if
not present; keep the same message so callers' behavior is unchanged but now
supports wrapping/formatting for variables like authID/authToken.
- Around line 469-472: Replace the plain errors.New usage in the GetCredential
error branch with fmt.Errorf so the original error is wrapped and preserved;
specifically, in the block after calling eService.vaultClient.GetCredential(ctx,
auth, credentialID) (variables cred, err, credentialID), return
fmt.Errorf("could not read the Vobiz SIP credential: %w", err) instead of
errors.New(...), and add an import for "fmt" if missing.
- Around line 450-453: Replace the errors.New call when parsing the SIP
credential ID with fmt.Errorf and wrap the original parse error for context:
after strconv.ParseUint(credIDStr, 10, 64) returns err, return
fmt.Errorf("invalid Vobiz SIP credential: %w", err) (referencing credIDStr,
credentialID and the ParseUint call) so the original error is preserved and
formatting is consistent.
- Around line 443-445: Replace the errors.New call with fmt.Errorf to follow the
project's error formatting convention: change return nil, errors.New("a
caller-ID phone number is required to enable inbound calls") to return nil,
fmt.Errorf("a caller-ID phone number is required to enable inbound calls") and
ensure the fmt package is imported (or add it to the import list if missing) in
assistant.deployment.impl.service.go; the affected check uses the did variable
that validates caller ID.

In `@api/assistant-api/sip/registration/record.go`:
- Line 35: Replace the string literals []string{"sip", "vobiz_sip"} used in the
GORM Where clause with the Telephony constants (e.g.
channel_telephony.SIP.String() and channel_telephony.VobizSIP.String()) so the
query becomes Where("telephony_provider IN ? AND status = ?",
[]string{channel_telephony.SIP.String(), channel_telephony.VobizSIP.String()},
type_enums.RECORD_ACTIVE); add the import alias channel_telephony from the
internal/channel/telephony package and ensure the code compiles and tests pass
after the change.

In `@api/assistant-api/sip/sip.go`:
- Line 580: Replace the string literals in the DB filter with the Telephony
constants: change the slice []string{"sip", "vobiz_sip"} to use
channel_telephony.SIP.String() and channel_telephony.VobizSIP.String() so the
query in the Where(...) call uses the defined constants; also add the import
alias channel_telephony
"github.com/rapidaai/api/assistant-api/internal/channel/telephony" at the top of
the file so the symbols resolve.
- Around line 81-83: The code calls
web_client.NewVaultClientGRPC(&config.AppConfig, logger, redis) twice; create
the Vault client once and reuse it by instantiating a single variable (e.g.,
vaultClient := web_client.NewVaultClientGRPC(&config.AppConfig, logger, redis))
and pass that same instance into
internal_assistant_service.NewAssistantDeploymentService(...) for the
deploymentService parameter and assign it to the engine's vaultClient field,
updating uses of deploymentService and vaultClient accordingly to avoid
duplicate initialization and gRPC connections.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3342ae23-68f0-4c57-971f-db3909204bc2

📥 Commits

Reviewing files that changed from the base of the PR and between 293c920 and e7c5124.

📒 Files selected for processing (26)
  • api/assistant-api/api/assistant-deployment/assistant_deployment.go
  • api/assistant-api/api/assistant-deployment/create_assistant_phone_deployment.go
  • api/assistant-api/api/talk/answer.go
  • api/assistant-api/internal/channel/telephony/internal/vobiz/internal/audio_processor.go
  • api/assistant-api/internal/channel/telephony/internal/vobiz/internal/status_callback.go
  • api/assistant-api/internal/channel/telephony/internal/vobiz/internal/types.go
  • api/assistant-api/internal/channel/telephony/internal/vobiz/telephony.go
  • api/assistant-api/internal/channel/telephony/internal/vobiz/websocket.go
  • api/assistant-api/internal/channel/telephony/outbound.go
  • api/assistant-api/internal/channel/telephony/streamer.go
  • api/assistant-api/internal/channel/telephony/telephony.go
  • api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go
  • api/assistant-api/router/assistant.go
  • api/assistant-api/sip/registration/record.go
  • api/assistant-api/sip/sip.go
  • api/web-api/internal/service/vault/service.go
  • api/web-api/internal/service/vault/vobiz.go
  • cmd/assistant/assistant.go
  • pkg/clients/vobiz/client.go
  • pkg/clients/vobiz/client_test.go
  • pkg/clients/vobiz/types.go
  • ui/src/app/components/base/modal/create-provider-credential-modal/index.tsx
  • ui/src/providers/provider.development.json
  • ui/src/providers/provider.production.json
  • ui/src/providers/vobiz_sip/telephony.json
  • ui/src/providers/vobiz_websocket/telephony.json

Comment thread api/assistant-api/api/talk/answer.go
Comment thread api/web-api/internal/service/vault/service.go Outdated
Comment thread api/web-api/internal/service/vault/vobiz.go Outdated
Comment on lines +55 to +57
if sipUsername == "" {
return nil, fmt.Errorf("vobiz sip_username is required")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Wrap validation error with context.

The error message is constructed with fmt.Errorf but doesn't wrap an underlying error. For consistency with the coding guidelines, use fmt.Errorf for all error returns.

🔧 Proposed fix
 	if sipUsername == "" {
-		return nil, fmt.Errorf("vobiz sip_username is required")
+		return nil, fmt.Errorf("vobiz: sip_username is required")
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/web-api/internal/service/vault/vobiz.go` around lines 55 - 57, The
validation check for sipUsername currently returns a plain fmt.Errorf string;
create a sentinel error (e.g. ErrMissingSIPUsername) and return a wrapped error
using fmt.Errorf with %w so callers can unwrap it. Update the check in vobiz.go
(the sipUsername validation) to return fmt.Errorf("vobiz sip_username is
required: %w", ErrMissingSIPUsername) instead of the bare message, and declare
ErrMissingSIPUsername = errors.New("missing sip_username") near the top of the
file.

Source: Coding guidelines

Comment thread api/web-api/internal/service/vault/vobiz.go Outdated
Comment thread api/web-api/internal/service/vault/vobiz.go Outdated
Comment thread pkg/clients/vobiz/client.go Outdated
Comment thread ui/src/app/components/base/modal/create-provider-credential-modal/index.tsx Outdated
Comment thread ui/src/app/components/base/modal/create-provider-credential-modal/index.tsx Outdated
Comment thread ui/src/providers/vobiz_websocket/telephony.json Outdated
- Fix provider-code checks in the credential modal (vobiz -> vobiz_sip) so sip_password is correctly optional and the provisioning success message shows.
- Make the answer-XML error log provider-agnostic (it serves any telephony provider).
- Wrap the crypto-rand error with %w in the SIP password generator.
- Use a generic E.164 placeholder for the vobiz_websocket caller-ID field.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/web-api/internal/service/vault/vobiz.go (1)

68-72: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Silent error swallowing may cause duplicate trunk creation.

If findExistingVobizSIP fails due to a transient DB error, the error is silently ignored and the code proceeds to provision a new trunk. This could create duplicate trunks in Vobiz for the same trunkName when the DB recovers.

Consider returning the error when the lookup fails, rather than only when err == nil:

🔧 Proposed fix
 	// Idempotency: reuse an existing trunk provisioned for the same name.
-	if existing, err := vs.findExistingVobizSIP(ctx, auth, trunkName); err == nil && existing != nil {
+	existing, err := vs.findExistingVobizSIP(ctx, auth, trunkName)
+	if err != nil {
+		return nil, err // already wrapped with context
+	}
+	if existing != nil {
 		vs.logger.Debugf("vobiz trunk %q already provisioned, reusing existing sip credential", trunkName)
 		return existing, nil
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/web-api/internal/service/vault/vobiz.go` around lines 68 - 72, The
current idempotency check silently swallows errors from vs.findExistingVobizSIP
leading to possible duplicate trunk creation; update the logic in the provision
flow so that if vs.findExistingVobizSIP(ctx, auth, trunkName) returns a non-nil
err you surface that error (e.g., return it up the call chain or wrap and
return) instead of proceeding to provision a new trunk, while keeping the
existing early-return when an existing record is found; locate the conditional
that calls vs.findExistingVobizSIP and add explicit error handling for err !=
nil (logging + return err) so transient DB failures won't allow duplicate
provisioning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@api/web-api/internal/service/vault/vobiz.go`:
- Around line 68-72: The current idempotency check silently swallows errors from
vs.findExistingVobizSIP leading to possible duplicate trunk creation; update the
logic in the provision flow so that if vs.findExistingVobizSIP(ctx, auth,
trunkName) returns a non-nil err you surface that error (e.g., return it up the
call chain or wrap and return) instead of proceeding to provision a new trunk,
while keeping the existing early-return when an existing record is found; locate
the conditional that calls vs.findExistingVobizSIP and add explicit error
handling for err != nil (logging + return err) so transient DB failures won't
allow duplicate provisioning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e54b9f45-f453-44d8-a128-77706fecaa91

📥 Commits

Reviewing files that changed from the base of the PR and between c986543 and 71b4458.

📒 Files selected for processing (1)
  • api/web-api/internal/service/vault/vobiz.go

When a vobiz_sip phone deployment enables inbound calls (rapida.sip_inbound=true), the deployment service auto-creates the Vobiz-side config via the REST API: an origination URI pointing at this assistant-api's configured SIP address (sip.external_ip:port), an inbound trunk referencing it, and assigns the deployment's DID — then persists rapida.inbound_trunk_id/uri_id so re-saves reuse the trunk. Vobiz failures (number already attached / not in account) surface to the UI via a typed InboundProvisioningError; all other errors keep the generic message, so non-vobiz flows are unchanged.

The Vobiz credential is read with a redis-free one-shot gRPC fetch (web.GetCredentialDirect), so no vault-client/redis wiring is threaded through the bootstrap/router/constructors. Adds CreateOriginationURI + AssignNumber + inbound trunk fields to the Vobiz client.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (2)
pkg/clients/web/vault_client.go (1)

146-157: ⚡ Quick win

Consider accepting a context parameter with timeout support.

The function uses context.Background() without any deadline. gRPC calls have no default timeout, so if the vault service becomes unresponsive, this call will hang indefinitely. The existing methods in this file (GetCredential, GetOauth2Credential) accept a context parameter, allowing callers to control timeouts and cancellation.

♻️ Suggested refactor to accept context with timeout
-func GetCredentialDirect(cfg *config.AppConfig, logger commons.Logger, auth types.SimplePrinciple, vaultId uint64) (*vault_api.VaultCredential, error) {
+func GetCredentialDirect(ctx context.Context, cfg *config.AppConfig, logger commons.Logger, auth types.SimplePrinciple, vaultId uint64) (*vault_api.VaultCredential, error) {
 	conn, err := grpc.NewClient(cfg.Web.Host, grpc.WithTransportCredentials(insecure.NewCredentials()))
 	if err != nil {
 		return nil, fmt.Errorf("vault: unable to connect to web-api: %w", err)
 	}
 	defer conn.Close()

 	token, err := types.CreateServiceScopeToken(auth, cfg.Secret)
 	if err != nil {
 		return nil, fmt.Errorf("vault: unable to create service token: %w", err)
 	}
-	ctx := metadata.NewOutgoingContext(context.Background(), metadata.New(map[string]string{types.SERVICE_SCOPE_KEY: token}))
+	ctx = metadata.NewOutgoingContext(ctx, metadata.New(map[string]string{types.SERVICE_SCOPE_KEY: token}))

Callers can then pass a context with timeout:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cred, err := web_client.GetCredentialDirect(ctx, cfg, logger, auth, vaultId)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/clients/web/vault_client.go` around lines 146 - 157, Update
GetCredentialDirect to accept a context.Context parameter (e.g., change
signature to GetCredentialDirect(ctx context.Context, cfg *config.AppConfig,
logger commons.Logger, auth types.SimplePrinciple, vaultId uint64)) and use that
context when creating the outgoing metadata context instead of
context.Background() (replace metadata.NewOutgoingContext(context.Background(),
...) with metadata.NewOutgoingContext(ctx, ...)). Ensure the function uses the
passed context for the gRPC call so callers can set timeouts/cancellation
(caller sites should create a context with timeout and pass it in) and keep
token creation via types.CreateServiceScopeToken and conn lifecycle unchanged.
api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go (1)

29-35: 💤 Low value

Consider unexporting the Message field.

The InboundProvisioningError.Message field is exported but only accessed via the Error() method. Unexported fields are preferred when direct external access isn't required.

♻️ Proposed refactor
-type InboundProvisioningError struct{ Message string }
+type InboundProvisioningError struct{ message string }

-func (e *InboundProvisioningError) Error() string { return e.Message }
+func (e *InboundProvisioningError) Error() string { return e.message }

Then update all instantiations to use lowercase message: field name.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 29 - 35, The InboundProvisioningError struct exposes its Message
field unnecessarily; change the struct to use an unexported field (rename
Message to message) and keep the Error() method as-is to surface the message;
then update every instantiation of InboundProvisioningError to set the
unexported field using the lowercase name (e.g.
InboundProvisioningError{message: "..."}), leaving calls to Error() unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`:
- Around line 427-570: Add a new *_test.go alongside
assistant.deployment.impl.service.go that provides unit tests for the new
provisioning logic: write table-driven tests for maybeProvisionVobizInbound
covering provider check, rapida.sip_inbound flag, missing phone/credential
parsing errors, and successful path (mock web_client.GetCredentialDirect to
return creds); unit tests for provisionVobizInbound that mock vobiz.Client
(CreateOriginationURI, CreateTrunk, AssignNumber, DeleteTrunk) to assert call
ordering, successful return, and rollback when AssignNumber fails; tests for
existingInboundTrunk that seed a fake DB/ORM with AssistantPhoneDeployment
entries to verify idempotency lookup; tests for vobizAssignError that construct
vobiz.VobizAPIError with different StatusCode values to assert mapped messages;
and a small test asserting InboundProvisioningError values; use dependency
injection/mocks for eService.provisionVobizInbound and vobiz client, and
table-driven subtests for clarity.
- Around line 477-482: The code uses cred.GetValue().AsMap() and then performs
unchecked type assertions for authID and authToken, allowing one missing or
wrong-typed field to slip through; update the extraction to verify types
explicitly (e.g., check that value["auth_id"] and value["auth_token"] exist and
are strings) and return an InboundProvisioningError with the same message if
either assertion fails or a field is missing/empty, so both type and presence
are enforced before proceeding.
- Around line 456-459: The parse error from strconv.ParseUint on credIDStr is
being swallowed; modify the error return so it preserves context by wrapping the
original err using fmt.Errorf with the %w verb and include it in the returned
InboundProvisioningError (e.g., construct the InboundProvisioningError to carry
the wrapped error or set its Message to a descriptive string and an Err field
containing fmt.Errorf("parsing credential ID %q: %w", credIDStr, err)); update
the block around credentialID, err := strconv.ParseUint(credIDStr, 10, 64) to
return the wrapped error instead of the generic message so callers can inspect
the underlying parsing error.
- Around line 558-570: The fallback return in vobizAssignError currently builds
an InboundProvisioningError with fmt.Sprintf which discards the original error
chain; change this so the original err is wrapped (use fmt.Errorf with "%w") or
modify InboundProvisioningError to carry a Cause error field and set it to err,
then return the InboundProvisioningError containing the wrapped/original error
so error unwrapping works (update vobizAssignError to wrap or set Cause and
ensure consumers still detect InboundProvisioningError).
- Around line 534-538: The DB query error is being swallowed; update the error
branch after
eService.postgres.DB(...).Preload("TelephonyOption").Where(...).Find(&deployments).Error
to log the error (include the error value and context such as assistantId and
telephony_provider "vobiz_sip") before returning; use the service logger (e.g.,
eService.logger.Errorf or similar) or fallback to log.Printf to emit a message
like "failed querying deployments for assistant %s provider vobiz_sip: %v" and
then return the same values so the failure is visible in logs.
- Around line 499-504: The error returned from client.CreateOriginationURI is
being converted to a string and wrapped in an InboundProvisioningError, which
loses error unwrapping; update the error handling in the CreateOriginationURI
call to wrap the original error using fmt.Errorf with the %w verb (e.g.,
fmt.Errorf("failed to create the Vobiz origination URI: %w", err)) so callers
can unwrap it, and if you must return an InboundProvisioningError preserve that
type while also returning or logging the wrapped error (or refactor
InboundProvisioningError to hold an inner error) so error chain information from
CreateOriginationURI is not lost.
- Around line 473-475: When GetCredentialDirect(&eService.cfg.AppConfig,
eService.logger, auth, credentialID) returns an error, preserve the original
error by wrapping it with fmt.Errorf rather than discarding it; replace the
current return that creates an InboundProvisioningError with something like:
create a wrappedErr := fmt.Errorf("could not read the Vobiz SIP credential
(credentialID=%s): %w", credentialID, err) and return nil, wrappedErr (or set
InboundProvisioningError to include the wrappedErr string) and add the fmt
import if missing so the original error chain from GetCredentialDirect is
preserved.
- Around line 506-518: The CreateTrunk error is being converted to a string via
fmt.Sprintf which loses the original error for unwrapping; update the error
return from the CreateTrunk call to preserve the error chain by wrapping the
original err with fmt.Errorf and the %w verb (e.g. return ...,
fmt.Errorf("failed to create the Vobiz inbound trunk: %w", err)) or
alternatively extend the InboundProvisioningError type to carry the underlying
error and set that field instead; locate the CreateTrunk invocation and the
return that constructs InboundProvisioningError and change it to wrap/attach the
original err so callers can unwrap it.

In `@pkg/clients/web/vault_client.go`:
- Around line 140-170: Add a new test file in the same package that exercises
GetCredentialDirect and covers (1) success returning a VaultCredential, (2)
service-level error returned via res.Error.HumanMessage, and (3) RPC-level
failure (e.g. client connection error); implement each test by starting a local
gRPC server that registers a fake implementation of vault_api.VaultServiceServer
which inspects the incoming metadata and responds to GetCredentialRequest
accordingly, then call GetCredentialDirect (which uses grpc.NewClient and
vault_api.NewVaultServiceClient) and assert the returned value/error matches the
scenario; ensure the tests build/run without external services by using an
in-process grpc.Server with insecure creds and distinct handlers that return a
populated GetCredentialResponse, a response with Error set, and by closing the
server early to simulate RPC errors.

---

Nitpick comments:
In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`:
- Around line 29-35: The InboundProvisioningError struct exposes its Message
field unnecessarily; change the struct to use an unexported field (rename
Message to message) and keep the Error() method as-is to surface the message;
then update every instantiation of InboundProvisioningError to set the
unexported field using the lowercase name (e.g.
InboundProvisioningError{message: "..."}), leaving calls to Error() unchanged.

In `@pkg/clients/web/vault_client.go`:
- Around line 146-157: Update GetCredentialDirect to accept a context.Context
parameter (e.g., change signature to GetCredentialDirect(ctx context.Context,
cfg *config.AppConfig, logger commons.Logger, auth types.SimplePrinciple,
vaultId uint64)) and use that context when creating the outgoing metadata
context instead of context.Background() (replace
metadata.NewOutgoingContext(context.Background(), ...) with
metadata.NewOutgoingContext(ctx, ...)). Ensure the function uses the passed
context for the gRPC call so callers can set timeouts/cancellation (caller sites
should create a context with timeout and pass it in) and keep token creation via
types.CreateServiceScopeToken and conn lifecycle unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 07f7946f-c0e7-4af5-be1b-073b5a6d0831

📥 Commits

Reviewing files that changed from the base of the PR and between 71b4458 and 5ae4b43.

📒 Files selected for processing (5)
  • api/assistant-api/api/assistant-deployment/create_assistant_phone_deployment.go
  • api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go
  • pkg/clients/vobiz/client.go
  • pkg/clients/vobiz/types.go
  • pkg/clients/web/vault_client.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/clients/vobiz/types.go
  • pkg/clients/vobiz/client.go

Comment on lines +427 to +570
// maybeProvisionVobizInbound provisions Vobiz inbound routing when a vobiz_sip
// deployment enables inbound calls: it creates an origination URI pointing at
// this assistant-api's SIP server (cfg.SIPConfig.ExternalIP:Port), an inbound
// trunk referencing it, and assigns the DID. Returns extra telephony options
// (inbound_trunk_id / inbound_uri_id) to persist. Non-vobiz / non-inbound paths
// early-return (nil, nil), so they are byte-for-byte unchanged.
func (eService assistantDeploymentService) maybeProvisionVobizInbound(
ctx context.Context, auth types.SimplePrinciple, assistantId uint64,
phoneProvider string, opts []*protos.Metadata,
) ([]*protos.Metadata, error) {
if phoneProvider != "vobiz_sip" {
return nil, nil
}
optMap := make(map[string]string, len(opts))
for _, o := range opts {
optMap[o.GetKey()] = o.GetValue()
}
if optMap["rapida.sip_inbound"] != "true" {
return nil, nil
}

did := optMap["phone"]
if did == "" {
return nil, &InboundProvisioningError{Message: "a caller-ID phone number is required to enable inbound calls"}
}
credIDStr := optMap["rapida.credential_id"]
if credIDStr == "" {
return nil, &InboundProvisioningError{Message: "a Vobiz SIP credential is required to enable inbound calls"}
}
credentialID, err := strconv.ParseUint(credIDStr, 10, 64)
if err != nil {
return nil, &InboundProvisioningError{Message: "invalid Vobiz SIP credential"}
}

// Idempotency: reuse an existing inbound trunk for the same DID if a prior
// deployment already provisioned one (avoids duplicate Vobiz trunks on re-save).
if trunkID, uriID := eService.existingInboundTrunk(ctx, assistantId, did); trunkID != "" {
eService.logger.Infof("vobiz inbound: reusing existing trunk %s for DID %s", trunkID, did)
return inboundMetadata(trunkID, uriID), nil
}

if eService.cfg.SIPConfig == nil || eService.cfg.SIPConfig.ExternalIP == "" {
return nil, &InboundProvisioningError{Message: "the assistant SIP server has no external IP configured; cannot provision inbound"}
}

// Read the Vobiz account credentials via a redis-free, one-shot gRPC fetch.
cred, err := web_client.GetCredentialDirect(&eService.cfg.AppConfig, eService.logger, auth, credentialID)
if err != nil {
return nil, &InboundProvisioningError{Message: "could not read the Vobiz SIP credential"}
}
value := cred.GetValue().AsMap()
authID, _ := value["auth_id"].(string)
authToken, _ := value["auth_token"].(string)
if authID == "" || authToken == "" {
return nil, &InboundProvisioningError{Message: "the Vobiz SIP credential is missing auth_id/auth_token"}
}

sipAddr := fmt.Sprintf("%s:%d", eService.cfg.SIPConfig.ExternalIP, eService.cfg.SIPConfig.Port)
trunkID, uriID, err := eService.provisionVobizInbound(ctx, authID, authToken, sipAddr, did)
if err != nil {
return nil, err // already an *InboundProvisioningError
}
return inboundMetadata(trunkID, uriID), nil
}

// provisionVobizInbound performs the three Vobiz API calls and rolls back the
// trunk if number assignment fails.
func (eService assistantDeploymentService) provisionVobizInbound(
ctx context.Context, authID, authToken, sipAddr, did string,
) (string, string, error) {
client := vobiz.NewClient()

uri, err := client.CreateOriginationURI(ctx, authID, authToken, vobiz.CreateOriginationURIRequest{
URI: sipAddr, Transport: "udp", Priority: 1, Weight: 10, Enabled: true,
})
if err != nil {
return "", "", &InboundProvisioningError{Message: fmt.Sprintf("failed to create the Vobiz origination URI: %v", err)}
}

trunk, err := client.CreateTrunk(ctx, authID, authToken, vobiz.CreateTrunkRequest{
Name: fmt.Sprintf("rapida-inbound-%s", did),
TrunkStatus: "enabled",
TrunkDirection: "inbound",
Transport: "udp",
ConcurrentCallsLimit: 10,
CpsLimit: 2,
PrimaryURIUUID: uri.ID,
InboundDestination: uri.ID,
})
if err != nil {
return "", "", &InboundProvisioningError{Message: fmt.Sprintf("failed to create the Vobiz inbound trunk: %v", err)}
}

if err := client.AssignNumber(ctx, authID, authToken, did, trunk.TrunkID); err != nil {
// Roll back the just-created trunk; surface a user-friendly message.
if delErr := client.DeleteTrunk(ctx, authID, authToken, trunk.TrunkID); delErr != nil {
eService.logger.Errorf("vobiz inbound: failed to roll back trunk %s: %v", trunk.TrunkID, delErr)
}
return "", "", vobizAssignError(did, err)
}
return trunk.TrunkID, uri.ID, nil
}

// existingInboundTrunk returns the inbound trunk/URI ids already provisioned for
// the given DID on an active vobiz_sip deployment of this assistant, if any.
func (eService assistantDeploymentService) existingInboundTrunk(ctx context.Context, assistantId uint64, did string) (string, string) {
var deployments []internal_assistant_entity.AssistantPhoneDeployment
if err := eService.postgres.DB(ctx).Preload("TelephonyOption").
Where("assistant_id = ? AND telephony_provider = ? AND status = ?", assistantId, "vobiz_sip", type_enums.RECORD_ACTIVE).
Find(&deployments).Error; err != nil {
return "", ""
}
for _, dep := range deployments {
o := dep.GetOptions()
phone, _ := o.GetString("phone")
trunkID, _ := o.GetString("rapida.inbound_trunk_id")
uriID, _ := o.GetString("rapida.inbound_uri_id")
if phone == did && trunkID != "" {
return trunkID, uriID
}
}
return "", ""
}

func inboundMetadata(trunkID, uriID string) []*protos.Metadata {
return []*protos.Metadata{
{Key: "rapida.inbound_trunk_id", Value: trunkID},
{Key: "rapida.inbound_uri_id", Value: uriID},
}
}

// vobizAssignError maps a Vobiz number-assignment failure to a clear UI message.
func vobizAssignError(did string, err error) error {
var apiErr *vobiz.VobizAPIError
if errors.As(err, &apiErr) {
switch apiErr.StatusCode {
case 404:
return &InboundProvisioningError{Message: fmt.Sprintf("phone number %s is not in your Vobiz account", did)}
case 400, 409:
return &InboundProvisioningError{Message: fmt.Sprintf("phone number %s is already attached to another Vobiz trunk — unlink it first", did)}
}
}
return &InboundProvisioningError{Message: fmt.Sprintf("failed to attach %s to the Vobiz inbound trunk: %v", did, err)}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Add test coverage for the new provisioning logic.

This file adds substantial new functionality (inbound provisioning workflow, error handling, idempotency, rollback logic) but no corresponding test file is included.

As per coding guidelines, backend changes in api/**, pkg/**, or cmd/** must include corresponding *_test.go updates in the same package.

Consider adding unit tests for:

  • maybeProvisionVobizInbound: validation logic, early returns, error paths
  • provisionVobizInbound: API call sequencing, rollback on failure
  • existingInboundTrunk: idempotency query logic
  • vobizAssignError: error mapping for different status codes
  • InboundProvisioningError: error type behavior
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 427 - 570, Add a new *_test.go alongside
assistant.deployment.impl.service.go that provides unit tests for the new
provisioning logic: write table-driven tests for maybeProvisionVobizInbound
covering provider check, rapida.sip_inbound flag, missing phone/credential
parsing errors, and successful path (mock web_client.GetCredentialDirect to
return creds); unit tests for provisionVobizInbound that mock vobiz.Client
(CreateOriginationURI, CreateTrunk, AssignNumber, DeleteTrunk) to assert call
ordering, successful return, and rollback when AssignNumber fails; tests for
existingInboundTrunk that seed a fake DB/ORM with AssistantPhoneDeployment
entries to verify idempotency lookup; tests for vobizAssignError that construct
vobiz.VobizAPIError with different StatusCode values to assert mapped messages;
and a small test asserting InboundProvisioningError values; use dependency
injection/mocks for eService.provisionVobizInbound and vobiz client, and
table-driven subtests for clarity.

Source: Coding guidelines

Comment thread pkg/clients/web/vault_client.go Outdated
Comment on lines +140 to +170
// GetCredentialDirect fetches a vault credential via a one-shot gRPC call to the
// vault service WITHOUT the redis-backed caching client. The internal
// service-scope auth token is a stateless JWT (no redis), so callers that don't
// hold a RedisConnector (e.g. the deployment service) can still read a
// credential. Intended for infrequent reads; it opens and closes its own
// connection per call.
func GetCredentialDirect(cfg *config.AppConfig, logger commons.Logger, auth types.SimplePrinciple, vaultId uint64) (*vault_api.VaultCredential, error) {
conn, err := grpc.NewClient(cfg.Web.Host, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, fmt.Errorf("vault: unable to connect to web-api: %w", err)
}
defer conn.Close()

token, err := types.CreateServiceScopeToken(auth, cfg.Secret)
if err != nil {
return nil, fmt.Errorf("vault: unable to create service token: %w", err)
}
ctx := metadata.NewOutgoingContext(context.Background(), metadata.New(map[string]string{types.SERVICE_SCOPE_KEY: token}))

res, err := vault_api.NewVaultServiceClient(conn).GetCredential(ctx, &vault_api.GetCredentialRequest{VaultId: vaultId})
if err != nil {
return nil, fmt.Errorf("vault: GetCredential failed: %w", err)
}
if res.GetSuccess() && res.GetData() != nil {
return res.GetData(), nil
}
if res.GetError() != nil {
return nil, fmt.Errorf("vault: %s", res.GetError().HumanMessage)
}
return nil, errors.New("vault: credential not found")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check for existing tests covering GetCredentialDirect in this package

# Search for test files in the same package
fd -e go -p 'test' pkg/clients/web/

# Search for any tests referencing GetCredentialDirect
rg -n 'GetCredentialDirect' --type go -g '*_test.go'

Repository: rapidaai/voice-ai

Length of output: 43


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== *_test.go under pkg/clients/web =="
find pkg/clients/web -maxdepth 5 -type f -name '*_test.go' | sort

echo
echo "== References to GetCredentialDirect in tests =="
rg -n --type go -g '*_test.go' 'GetCredentialDirect' pkg/clients/web || true

echo
echo "== References to GetCredentialDirect in non-tests (sanity) =="
rg -n --type go 'GetCredentialDirect' pkg/clients/web || true

echo
echo "== Confirm file under review exists =="
ls -l pkg/clients/web/vault_client.go

Repository: rapidaai/voice-ai

Length of output: 640


Add unit tests for GetCredentialDirect in pkg/clients/web

No *_test.go files (and no GetCredentialDirect references) exist under pkg/clients/web, so success/error/not-found behavior for the exported helper isn’t covered. Add a *_test.go in the same package with unit tests for these scenarios.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/clients/web/vault_client.go` around lines 140 - 170, Add a new test file
in the same package that exercises GetCredentialDirect and covers (1) success
returning a VaultCredential, (2) service-level error returned via
res.Error.HumanMessage, and (3) RPC-level failure (e.g. client connection
error); implement each test by starting a local gRPC server that registers a
fake implementation of vault_api.VaultServiceServer which inspects the incoming
metadata and responds to GetCredentialRequest accordingly, then call
GetCredentialDirect (which uses grpc.NewClient and
vault_api.NewVaultServiceClient) and assert the returned value/error matches the
scenario; ensure the tests build/run without external services by using an
in-process grpc.Server with insecure creds and distinct handlers that return a
populated GetCredentialResponse, a response with Error set, and by closing the
server early to simulate RPC errors.

Source: Coding guidelines

- InboundProvisioningError now carries its underlying cause (Unwrap) so error chains are preserved; constructors pass the cause and the handler logs it.
- Log (instead of silently swallowing) a failed idempotency lookup so a DB error can't masquerade as 'no existing trunk' and cause duplicate provisioning.
- Harden the credential type assertions (check the assertion ok flags).
- Add Vobiz client tests for CreateOriginationURI and AssignNumber (incl. the already-attached error path).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go (1)

352-356: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Make Vobiz provisioning and deployment persistence atomic.

maybeProvisionVobizInbound can fully create/attach the inbound trunk before archiveDeploymentRecords, db.Create(deployment), and the telephony-option insert run. If any of those DB steps fail, the request returns an error but the DID stays provisioned in Vobiz with no persisted deployment/metadata to rediscover on retry. That leaves orphaned external state and makes the next attempt non-idempotent.

This needs one compensating strategy across the whole flow: either persist inside a DB transaction and clean up Vobiz resources on rollback, or switch to a pending-state/outbox flow so external side effects happen only after durable local state exists.

Also applies to: 379-425

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 352 - 356, The Vobiz provisioning call in
maybeProvisionVobizInbound currently runs before persisting deployment state,
risking orphaned external resources; change the flow so external provisioning is
only performed after durable DB state or is wrapped in a transaction with
compensating cleanup: either (A) create the deployment record and insert
telephony options (archiveDeploymentRecords, db.Create(deployment), and the
telephony-option insert) inside a DB transaction first, then call
maybeProvisionVobizInbound and on failure rollback/cleanup the transaction and
call a Vobiz cleanup helper to deprovision; or (B) adopt an outbox/pending-state
pattern where you persist a pending deployment row and outbox event, commit, and
only then run maybeProvisionVobizInbound asynchronously; update caller logic
where maybeProvisionVobizInbound is invoked (also for the similar block around
lines handling 379-425) to use one of these approaches and add a Vobiz
deprovision helper to guarantee cleanup on rollback/failure.
♻️ Duplicate comments (1)
api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go (1)

539-545: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t continue provisioning when the idempotency lookup fails.

Logging helps, but returning ("", "") still turns a DB failure into “no existing trunk”, so the caller provisions a new Vobiz trunk/assignment on the next line. That preserves the duplicate-provisioning bug this lookup is meant to prevent. Bubble the query error up and abort the create instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`
around lines 539 - 545, The idempotency DB lookup failure is being swallowed by
returning ("", ""), which forces provisioning to proceed; modify the handler
that calls
eService.postgres.DB(...).Preload("TelephonyOption").Where(...).Find(&deployments).Error
so that on err you return/propagate the error instead of returning empty
strings—i.e. stop returning ("", "") after logger.Errorf and instead return an
error (wrap err with context mentioning assistantId and did), update the method
signature (and its callers) to return an error (e.g. (string,string,error) or
propagate via existing error path), and ensure the caller aborts create when
this query fails; keep the logger.Errorf call but do not convert DB errors into
“no existing trunk”.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/clients/vobiz/client_test.go`:
- Around line 138-140: The test is asserting the decoded server path via
r.URL.Path so it doesn't verify that the DID was percent-encoded on the wire;
change the assertion in the test (the check around r.URL.Path in the test
function) to check r.RequestURI or r.URL.RawPath and assert that it contains
"%2B" (e.g., verify "/api/v1/Account/AUTH_ID/numbers/%2B919262171438/assign")
instead of using r.URL.Path.

---

Outside diff comments:
In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`:
- Around line 352-356: The Vobiz provisioning call in maybeProvisionVobizInbound
currently runs before persisting deployment state, risking orphaned external
resources; change the flow so external provisioning is only performed after
durable DB state or is wrapped in a transaction with compensating cleanup:
either (A) create the deployment record and insert telephony options
(archiveDeploymentRecords, db.Create(deployment), and the telephony-option
insert) inside a DB transaction first, then call maybeProvisionVobizInbound and
on failure rollback/cleanup the transaction and call a Vobiz cleanup helper to
deprovision; or (B) adopt an outbox/pending-state pattern where you persist a
pending deployment row and outbox event, commit, and only then run
maybeProvisionVobizInbound asynchronously; update caller logic where
maybeProvisionVobizInbound is invoked (also for the similar block around lines
handling 379-425) to use one of these approaches and add a Vobiz deprovision
helper to guarantee cleanup on rollback/failure.

---

Duplicate comments:
In
`@api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go`:
- Around line 539-545: The idempotency DB lookup failure is being swallowed by
returning ("", ""), which forces provisioning to proceed; modify the handler
that calls
eService.postgres.DB(...).Preload("TelephonyOption").Where(...).Find(&deployments).Error
so that on err you return/propagate the error instead of returning empty
strings—i.e. stop returning ("", "") after logger.Errorf and instead return an
error (wrap err with context mentioning assistantId and did), update the method
signature (and its callers) to return an error (e.g. (string,string,error) or
propagate via existing error path), and ensure the caller aborts create when
this query fails; keep the logger.Errorf call but do not convert DB errors into
“no existing trunk”.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 15132a85-b4e8-459e-b517-bb8219040b25

📥 Commits

Reviewing files that changed from the base of the PR and between 5ae4b43 and 5da9c78.

📒 Files selected for processing (3)
  • api/assistant-api/api/assistant-deployment/create_assistant_phone_deployment.go
  • api/assistant-api/internal/services/assistant/assistant.deployment.impl.service.go
  • pkg/clients/vobiz/client_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/assistant-api/api/assistant-deployment/create_assistant_phone_deployment.go

Comment thread pkg/clients/vobiz/client_test.go Outdated
@iamprashant iamprashant self-requested a review June 15, 2026 09:20

@iamprashant iamprashant left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Piyush-sahoo Thanks for submitting PR.

If adding a branded telephony, we only allow websocket other channels are very generic so any provider can be used.

  1. Changes should only be in telephony/internal/{provider}**
    a. Implement how call will be handled
    b. Transfer / end call / Status Callback
  2. Update in Factory to have new provider
  3. In UI update json only a provider taged as Telephony and credential needed to connect.

You can use codex/claude skill for integrating web-socket based telephony.

Address maintainer review on rapidaai#141: branded telephony providers are
WebSocket-only. Remove the vobiz_sip auto-provisioning path (trunk /
origination-URI / number-assignment via the Vobiz API, vault and
SIP-engine wiring) and keep only the vobiz_websocket provider, its
factory registration, and the UI provider config.

- drop VobizSIP factory entry; revert SIP-engine + outbound changes
- remove web-api vault provisioning + deployment auto-provision service
- trim pkg/clients/vobiz to the MakeCall surface the WS provider needs
- remove vobiz_sip from the UI provider registry
Move the Vobiz REST client (MakeCall) from pkg/clients/vobiz into
telephony/internal/vobiz/internal so the provider is self-contained like
twilio/telnyx — no shared pkg dependency, uses net/http directly. Keeps
all provider code within telephony/internal/{provider} per review.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants