The async training job keeps failing on the second polling interval with a 422 UNPROCESSABLE ENTITY when we pass the TRAINING CONFIG payload. We’ve verified the UTTERANCE EXAMPLES and INTENT LABELS match the schema, yet the CLASS IMBALANCE CONSTRAINT rejects the distribution. Current specs include:
- Python 3.9 requests library
- /api/v2/nlu/intent-models/train endpoint
- Snapshot comparison enabled for MODEL VERSIONING
- Audit logging routed to S3
The status polling loop returns a 503 after the third check, and the DEPLOYMENT PIPELINE reference never updates.
import requests
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
payload = {
"intentLabels": ["order_status", "refund_request"],
"utteranceExamples": [{"text": "where is my package", "intent": "order_status"}],
"classImbalanceThreshold": 0.15,
"enableAuditLog": True
}
resp = requests.post(f"{base_url}/api/v2/nlu/intent-models/train", json=payload, headers=headers)
print(resp.status_code, resp.json())
Documentation states: “Async training jobs require a valid access token with nlu:intent:edit and nlu:training:edit scopes throughout the entire lifecycle of the job.”
Why does it not work? You’re seeing a 422 turn into a 503. That’s usually the platform rejecting the poll because the token context is stale. The snapshot comparison needs higher privileges than a basic view scope.
Cause:
Your token rotation is lagging. The initial POST works because the token is fresh. By the second poll, the token service hasn’t refreshed the bearer token, or the scope doesn’t cover the audit logging payload size. The 503 is the platform giving up on the invalid auth context.
Solution:
Check the token scopes. You need to ensure the grant includes nlu:training:edit. Run this to verify the current token’s claims before hitting the poll endpoint.
import requests
# Verify token scopes first
token_response = requests.get('https://api.mypurecloud.com/api/v2/oauth/tokeninfo', headers={'Authorization': f'Bearer {access_token}'})
print(token_response.json())
# If nlu:training:edit is missing, you'll get 422 on poll
poll_url = f"https://api.mypurecloud.com/api/v2/nlu/intent-models/train/{job_id}/status"
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
# Ensure you handle the refresh token immediately upon 401, don't wait for 503
response = requests.get(poll_url, headers=headers)
The docs also mention: “If the training job payload exceeds the default size limit, the server returns 422 UNPROCESSABLE ENTITY.” You might be hitting that with the snapshot data. Chunk the utterances.
Weird how the 503 pops up. Usually means the backend worker crashed on the auth check.