This endpoint provides real-time cloud credentials verification progress over a WebSocket connection using structured JSON messages.
All messages are serialized as JSON with camelCase property names and inherit from ControlUp.Foundry.Domain.Models.Verification.Messages.VerificationMessage.
Message Types (see Domain.Models.Verification.Messages namespace):
- ControlUp.Foundry.Domain.Models.Verification.Messages.VerificationStartedMessage: Verification process begins
- Properties:
type,timestamp
- Properties:
- ControlUp.Foundry.Domain.Models.Verification.Messages.StepStartedMessage: A verification step begins
- Properties:
type,timestamp,stepNumber,stepName
- Properties:
- ControlUp.Foundry.Domain.Models.Verification.Messages.StepCompletedMessage: A verification step completes
- Properties:
type,timestamp,stepNumber,stepName,status,duration,detail statusvalues: "success" | "warning" | "failure"
- Properties:
- ControlUp.Foundry.Domain.Models.Verification.Messages.VerificationCompletedMessage: Verification process ends
- Properties:
type,timestamp,success,duration,errorMessage
- Properties:
Usage Flow:
- Client establishes WebSocket connection to
/api/v1/cloud/subscriptions/{id}/credentials/verify/stream - Server validates subscription exists and user has permission
- Server accepts WebSocket connection (HTTP 101 Switching Protocols)
- Server sends ControlUp.Foundry.Domain.Models.Verification.Messages.VerificationStartedMessage
- For each verification step (provider-specific):
- Server sends ControlUp.Foundry.Domain.Models.Verification.Messages.StepStartedMessage
- Server performs verification against cloud provider API
- Server sends ControlUp.Foundry.Domain.Models.Verification.Messages.StepCompletedMessage with results
- Server sends ControlUp.Foundry.Domain.Models.Verification.Messages.VerificationCompletedMessage
- Server closes WebSocket with normal closure or error status
WebSocket Client Example (JavaScript):
const subscriptionId = '123e4567-e89b-12d3-a456-426614174000';
const ws = new WebSocket(`wss://api.example.com/api/v1/cloud/subscriptions/${subscriptionId}/credentials/verify/stream`);
ws.onopen = () => {
console.log('WebSocket connected - waiting for verification to start...');
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
switch(message.type) {
case 'verification_started':
console.log('Verification started at:', message.timestamp);
// Initialize UI for verification progress
break;
case 'step_started':
console.log(`Step ${message.stepNumber}: ${message.stepName} - started`);
// Show spinner/progress indicator for this step
break;
case 'step_completed':
console.log(`Step ${message.stepNumber}: ${message.stepName} - ${message.status} in ${message.duration}`);
if (message.detail) {
console.log(` Detail: ${message.detail}`);
}
// Update UI with status icon: ✓ (success), ⚠ (warning), ✗ (failure)
break;
case 'verification_completed':
console.log(`Verification ${message.success ? 'succeeded' : 'failed'} - Duration: ${message.duration}`);
if (message.errorMessage) {
console.error(`Error: ${message.errorMessage}`);
}
// Finalize UI with overall result
break;
default:
console.warn('Unknown message type:', message.type);
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
// Handle connection errors
};
ws.onclose = (event) => {
console.log(`WebSocket closed - Code: ${event.code}, Reason: ${event.reason}`);
// Clean up UI and resources
};Example Message Sequence (Azure Subscription):
{"type":"verification_started","timestamp":"2025-10-23T10:30:00.000Z"}
{"type":"step_started","stepNumber":1,"stepName":"Validating Azure credentials format","timestamp":"2025-10-23T10:30:00.100Z"}
{"type":"step_completed","stepNumber":1,"stepName":"Validating Azure credentials format","status":"success","duration":"00:00:00.150","timestamp":"2025-10-23T10:30:00.250Z"}
{"type":"step_started","stepNumber":2,"stepName":"Testing connectivity to Azure Resource Manager","timestamp":"2025-10-23T10:30:00.300Z"}
{"type":"step_completed","stepNumber":2,"stepName":"Testing connectivity to Azure Resource Manager","status":"success","duration":"00:00:01.200","detail":"Successfully authenticated with tenant ID: abc123","timestamp":"2025-10-23T10:30:01.500Z"}
{"type":"step_started","stepNumber":3,"stepName":"Verifying required Azure permissions","timestamp":"2025-10-23T10:30:01.550Z"}
{"type":"step_completed","stepNumber":3,"stepName":"Verifying required Azure permissions","status":"failure","duration":"00:00:00.800","detail":"Missing required role: Virtual Machine Contributor","timestamp":"2025-10-23T10:30:02.350Z"}
{"type":"verification_completed","success":false,"duration":"00:00:02.500","errorMessage":"Verification failed","timestamp":"2025-10-23T10:30:02.500Z"}Notes:
- All timestamps are in ISO 8601 UTC format
- Duration fields are in TimeSpan format (hh:mm:ss.fffffff)
- Step numbers are sequential, starting from 1
- The
detailfield in ControlUp.Foundry.Domain.Models.Verification.Messages.StepCompletedMessage is optional (null for success, may contain info for warnings/failures) - WebSocket closes automatically after sending ControlUp.Foundry.Domain.Models.Verification.Messages.VerificationCompletedMessage
- If an error occurs during verification, the server will attempt to close the WebSocket with appropriate status code
101Switching Protocols - WebSocket connection established.
200OK
400Bad request. Invalid subscription ID or not a WebSocket request.
401Unauthorized. User is not authenticated.
403Forbidden. User does not have permission to view subscriptions in this organization.
404Not found. The specified cloud subscription does not exist or does not belong to the current organization.
500Internal server error occurred during verification.
