The 400 error you’re encountering is due to incomplete understanding of the Recipe object’s validation requirements. Let me provide a complete solution addressing all three focus areas.
1. API required fields validation: The Recipe type has both explicit required fields (documented) and implicit required fields (enforced by business logic). The API performs multi-layer validation:
- Schema validation (data types, field names)
- Business rule validation (mandatory relationships, lifecycle constraints)
- Type-specific validation (Recipe-specific requirements)
To see the complete validation rules, query the metadata:
GET /Windchill/servlet/odata/RecipeMgmt/$metadata
Filter on EntityType name="Recipe"
This returns the OData schema including all required properties marked with Nullable="false".
2. Recipe schema discovery: The actual required fields for Recipe creation are:
- name (String) - Recipe identifier
- recipeType (Enum) - Manufacturing, Assembly, Inspection, etc.
- containerReference (Object reference) - Parent product or project
- lifecycleState (Object) - Initial lifecycle state
- organizationUnit (Object reference) - Owning org unit
- creatorRole (String) - Role context for creator
Discover these dynamically:
OPTIONS /Windchill/servlet/odata/RecipeMgmt/Recipes
Response includes: Required-Fields header listing mandatory attributes
Alternatively, create a recipe via UI and capture the network traffic. The UI’s POST request reveals the complete payload structure.
3. Payload construction: Build your payload with all required fields:
{
"name": "Assembly_Process_v2",
"description": "Updated assembly sequence",
"recipeType": "Manufacturing",
"containerReference": {
"@odata.id": "Products('OR:wt.part.WTPart:123456')"
},
"lifecycleState": {
"@odata.id": "LifecycleStates('OR:wt.lifecycle.State:789')"
},
"organizationUnit": {
"@odata.id": "OrgUnits('OR:wt.org.WTOrganization:456')"
},
"creatorRole": "DESIGNER",
"owner": {
"@odata.id": "Users('demo.user')"
}
}
Step-by-step resolution:
- Discover container context: Recipes must belong to a product or project. Query available containers:
GET /Windchill/servlet/odata/ProdMgmt/Products
Select a product OID to use as containerReference
- Get default lifecycle state: Recipes require an initial lifecycle state:
GET /Windchill/servlet/odata/LifecycleMgmt/LifecycleTemplates?$filter=name eq 'Recipe'
Expand the template to get the initial state OID
- Determine organization unit: Query your user’s default org unit:
GET /Windchill/servlet/odata/OrgMgmt/Users('your.username')?$expand=DefaultOrganization
Extract the organization OID
- Construct complete payload: Combine all required references into your POST request.
Validation debugging technique: Use partial payload testing:
// Test 1: Minimal documented fields
{"name": "Test", "recipeType": "Manufacturing"}
// Note which field is reported missing
// Test 2: Add container
{...previous fields..., "containerReference": {...}}
// Continue adding fields until validation passes
Production-ready payload template:
{
"@odata.type": "#PTC.RecipeMgmt.Recipe",
"name": "Assembly_Process_v2",
"description": "Updated assembly sequence",
"recipeType": "Manufacturing",
"version": "A",
"containerReference": {"@odata.id": "Products('OR:wt.part.WTPart:123456')"},
"lifecycleState": {"@odata.id": "LifecycleStates('OR:wt.lifecycle.State:789')"},
"organizationUnit": {"@odata.id": "OrgUnits('OR:wt.org.WTOrganization:456')"},
"creatorRole": "DESIGNER",
"owner": {"@odata.id": "Users('demo.user')"},
"folder": {"@odata.id": "Folders('OR:wt.folder.Cabinet:default')"},
"teamTemplate": {"@odata.id": "TeamTemplates('OR:wt.team.TeamTemplate:default')"}
}
The @odata.type hint helps the API correctly identify the entity type. The version field (defaults to “A” if omitted) should be explicit. Folder and teamTemplate references ensure proper access control setup.
Test this payload structure first with a single recipe creation, then scale to your batch automation process.