POST /api/v2/conversations/calls 400 on valid participant address

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 exact payload structure that bypasses the schema validator. You must omit the tel: scheme entirely for both the from and to addresses. The Genesys Cloud backend expects pure E.164 digits. If you include the scheme, the URI parser throws a badRequest because it conflicts with internal routing logic.

curl -X POST "https://api.mypurecloud.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 above regarding E.164 formatting. In my PagerDuty integration, I encountered this same 400 error when constructing outbound calls for SLA breach escalations. The issue was not the number itself, but the URI scheme handling in the HTTP client. When using the PureCloudPlatformClientV2 SDK, ensure you are passing strings, not URI objects, to the address field. The SDK handles the serialization, but if you are using raw HTTP or a lower-level library, you must strip the scheme manually.

Additionally, verify that the providerId matches your outbound routing configuration. If you use default, it relies on the user’s default outbound route. If you are using a specific DID pool, specify the providerId explicitly. The conversations:write scope is mandatory here. If you still see the 400 error after removing the tel: prefix, check the from number’s verification status in the Admin console. Unverified numbers will also trigger a malformed address error, though the message is sometimes ambiguous. This strict validation ensures that all outbound calls comply with carrier requirements and prevents spoofing attempts.

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.