So I’m seeing a very odd bug with POST /api/v2/conversations/calls returning a 400 Bad Request despite the payload validating against the OpenAPI spec. I am using client_credentials auth and sending a JSON body with to set to tel:+2348000000000 and from set to my verified outbound number, yet the response consistently returns {"code":"badRequest","message":"Malformed participant address"}. The same endpoint works fine in Postman with identical headers and payload, ruling out network issues. Is there a specific formatting requirement for the to field in the CXone API that differs from standard RFC 3966, or is this a known issue with the current API version?
Check the to address format strictly. The API rejects tel:+2348000000000 because the tel: scheme is often stripped or mishandled in certain SDK versions or raw HTTP clients, leading to a malformed URI structure in the backend validator. Use pure E.164 format without the scheme.
Try this payload structure instead. I have seen this exact 400 error when the client sends the tel: prefix, especially if the from address is also wrapped in it. Ensure both from and to are plain strings.
{
"to": {
"address": "+2348000000000",
"addressType": "TELEPHONE"
},
"from": {
"address": "+1234567890",
"addressType": "TELEPHONE"
},
"type": "OUTBOUND"
}
If you are using the Python SDK, avoid passing tel:+... directly to the to parameter. Construct the ParticipantAddress object explicitly to ensure the schema validation passes on the server side.
from purecloudplatformclientv2 import ParticipantAddress, CreateConversationCallRequest
to_addr = ParticipantAddress(
address="+2348000000000",
address_type="TELEPHONE"
)
from_addr = ParticipantAddress(
address="+1234567890",
address_type="TELEPHONE"
)
body = CreateConversationCallRequest(
to=to_addr,
from_=from_addr,
type="OUTBOUND"
)
api_response = api_instance.post_conversations_calls(body=body)
Also, verify that the destination number is not flagged as invalid in your routing rules. If the number is blocked by a global policy, the error message can sometimes be misleading. Check the webhook event logs for any delivery failures on that specific number pattern. The 400 error usually points to syntax, but a 403 might indicate policy rejection. Start with the address format fix first.
This is caused by strict schema validation rejecting the tel: URI scheme. Remove the prefix and send pure E.164 digits for both from and to fields.
The raw curl command below demonstrates the payload structure that bypasses the schema validator. You must omit the tel: scheme entirely for both the from and to addresses. The CXone backend expects pure E.164 digits. Including the scheme causes a badRequest because it conflicts with internal routing logic.
curl -X POST "https://api.cxone.com/api/v2/conversations/calls" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"from": {
"address": "+15550100",
"name": "Outbound Caller"
},
"to": {
"address": "+2348000000000",
"name": "Customer"
},
"providerId": "default"
}'
This aligns with the suggestion regarding E.164 formatting. I ran into the same 400 error when building outbound calls for automated workflows. The issue wasn’t the number itself, but how the URI scheme was being handled. If you’re using the CXone SDK, ensure you’re passing strings, not URI objects, to the address field. The SDK should handle serialization, but if you’re using a lower-level HTTP client, strip the scheme manually.
Also, verify the providerId matches your outbound routing configuration within CXone’s outbound dialer settings. If you use default, it relies on the user’s default outbound route. If you’re using a specific DID pool, specify the providerId explicitly. The conversations:write permission is required. If you still get the 400 error after removing the tel: prefix, double-check the verification status of the from number in the CXone Admin console. Unverified numbers will also trigger a malformed address error, though the message can be unclear. This strict validation ensures outbound calls meet carrier requirements and prevents potential spoofing.
I typically get around this by stripping the scheme in the Data Action before the call. The backend validator is strict on E.164. Use a simple JSONPath transform to ensure clean digits.
{
"to": "$.input.to.replace('tel:', '')"
}
Note: Always validate the output schema before sending to the API to avoid silent failures.