This technical guide will walk you through how to create and configure MuleSoft projects that integrate with Procore’s construction project management platform. The Procore connector enables enterprise applications to create and manage construction projects through MuleSoft-based solutions.
Component | Version |
Mule Runtime | 4.9.6 or higher |
Java | 17 |
Connector | 0.0.115-SNAPSHOT |
Before you create a Mule project with the Procore connector, ensure you have the following:
Add the Procore connector dependency to your pom.xml:
<dependency> <groupId>com.procore</groupId> <artifactId>procore-connector</artifactId> <version>0.0.115-SNAPSHOT</version> <classifier>mule-plugin</classifier> </dependency>
Create a src/main/resources/properties/dev.properties file :
# HTTP Listener Configuration http.listener.host=0.0.0.0 http.listener.port=8081 # Procore OAuth Configuration procore.config.oauth.consumer_key=your-consumer-key procore.config.oauth.consumer_secret=your-consumer-secret procore.config.oauth.callback_path=/mule/callback procore.config.oauth.authorize_path=/authorize-procore procore.config.oauth.external_callback_path=http://localhost:8081/mule/callback
Configure the HTTP listener for your Mule application:
<http:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config"> <http:listener-connection host="${http.listener.host}" port="${http.listener.port}" /> </http:listener-config>
Configure the Procore connector with OAuth 2.0 authentication:
<procore:config name="Procore_Config" doc:name="Procore Config"> <procore:connection> <procore:oauth-authorization-code consumerKey="${procore.config.oauth.consumer_key}" consumerSecret="${procore.config.oauth.consumer_secret}"/> <procore:oauth-callback-config listenerConfig="HTTP_Listener_config" callbackPath="${procore.config.oauth.callback_path}" authorizePath="${procore.config.oauth.authorize_path}" externalCallbackUrl="${procore.config.oauth.external_callback_path}" /> </procore:connection> </procore:config>
Create separate property files for different environments:
http.listener.host=0.0.0.0 http.listener.port=8081 procore.config.oauth.external_callback_path=http://localhost:8081/mule/callback
http.listener.host=0.0.0.0 http.listener.port=8081 procore.config.oauth.external_callback_path=https://staging.yourdomain.com/mule/callback
http.listener.host=0.0.0.0 http.listener.port=8081 procore.config.oauth.external_callback_path=https://yourdomain.com/mule/callback
Create a flow to handle project creation requests:
<flow name="createProjectFlow"> <http:listener config-ref="HTTP_Listener_config" path="/create-project"/> <!-- Validate input payload --> <validation:is-not-null value="#[payload.company_id]" message="Company ID is required"/> <validation:is-not-null value="#[payload.name]" message="Project name is required"/> <!-- Transform payload if needed --> <ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/java --- { companyId: payload.company_id, name: payload.name, address: payload.address, city: payload.city, stateCode: payload.state_code, countryCode: payload.country_code, zip: payload.zip, description: payload.description, startDate: payload.start_date, completionDate: payload.completion_date, totalValue: payload.total_value }]]></ee:set-payload> </ee:message> </ee:transform> <!-- Call Procore API --> <procore:create-project config-ref="Procore_Config" companyId="#[payload.companyId]" name="#[payload.name]" address="#[payload.address]" city="#[payload.city]" stateCode="#[payload.stateCode]" countryCode="#[payload.countryCode]" zip="#[payload.zip]" description="#[payload.description]" startDate="#[payload.startDate]" completionDate="#[payload.completionDate]" totalValue="#[payload.totalValue]"/> <!-- Handle response --> <ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/json --- { success: true, projectId: payload.id, projectName: payload.name, message: "Project created successfully" }]]></ee:set-payload> </ee:message> </ee:transform> </flow>
Create a flow to handle project updates:
<flow name="updateProjectFlow"> <http:listener config-ref="HTTP_Listener_config" path="/update-project"/> <!-- Validate required parameters --> <validation:is-not-null value="#[payload.project_id]" message="Project ID is required"/> <validation:is-not-null value="#[payload.company_id]" message="Company ID is required"/> <!-- Call Procore API --> <procore:update-project config-ref="Procore_Config" companyId="#[payload.company_id]" projectId="#[payload.project_id]" name="#[payload.name]" active="#[payload.active]" address="#[payload.address]" city="#[payload.city]" description="#[payload.description]" startDate="#[payload.start_date]" completionDate="#[payload.completion_date]" totalValue="#[payload.total_value]"/> <!-- Handle response --> <ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/json --- { success: true, projectId: payload.id, projectName: payload.name, message: "Project updated successfully" }]]></ee:set-payload> </ee:message> </ee:transform> </flow>
Create a global error handler for your application:
<error-handler name="GlobalErrorHandler"> <on-error-continue type="PROCORE:BAD_REQUEST"> <ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/json --- { success: false, error: "Bad Request", message: error.description, details: error.detailedDescription }]]></ee:set-payload> </ee:message> </ee:transform> <http:response-builder statusCode="400"/> </on-error-continue> <on-error-continue type="PROCORE:UNAUTHORIZED"> <ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/json --- { success: false, error: "Unauthorized", message: "Invalid or expired authentication credentials" }]]></ee:set-payload> </ee:message> </ee:transform> <http:response-builder statusCode="401"/> </on-error-continue> <on-error-continue type="PROCORE:FORBIDDEN"> <ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/json --- { success: false, error: "Forbidden", message: "Insufficient permissions to access the resource" }]]></ee:set-payload> </ee:message> </ee:transform> <http:response-builder statusCode="403"/> </on-error-continue> <on-error-continue type="PROCORE:NOT_FOUND"> <ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/json --- { success: false, error: "Not Found", message: "The requested resource was not found" }]]></ee:set-payload> </ee:message> </ee:transform> <http:response-builder statusCode="404"/> </on-error-continue> <on-error-continue type="ANY"> <ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/json --- { success: false, error: "Internal Server Error", message: "An unexpected error occurred" }]]></ee:set-payload> </ee:message> </ee:transform> <http:response-builder statusCode="500"/> </on-error-continue> </error-handler>
Create a DataWeave transformation to validate and transform input data:
<ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/java --- { companyId: payload.company_id default "", name: payload.name default "", address: payload.address default "", city: payload.city default "", stateCode: payload.state_code default "", countryCode: payload.country_code default "US", zip: payload.zip default "", description: payload.description default "", startDate: payload.start_date default "", completionDate: payload.completion_date default "", totalValue: payload.total_value default 0.0, latitude: payload.latitude default "", longitude: payload.longitude default "", phone: payload.phone default "", projectNumber: payload.project_number default "", active: payload.active default true }]]></ee:set-payload> </ee:message> </ee:transform>
Create a DataWeave transformation for API responses:
<ee:transform> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/json --- { success: true, data: { id: payload.id, name: payload.name, description: payload.description, active: payload.active, address: payload.address, city: payload.city, stateCode: payload.stateCode, countryCode: payload.countryCode, zip: payload.zip, startDate: payload.startDate, completionDate: payload.completionDate, totalValue: payload.totalValue, createdAt: payload.createdAt, updatedAt: payload.updatedAt }, message: "Operation completed successfully" }]]></ee:set-payload> </ee:message> </ee:transform>
For local development:
For CloudHub deployment:
ISSUE | SOLUTION |
UNAUTHORIZED Error | Check the consumer key and secret key for OAuth. |
ACCESS_TOKEN_MISSING | Verify the configuration for token refresh. |
OAUTH_STATE_MISSING | Ensure that the OAuth flow is configured correctly. |
Scope Issues | Ensure that the requested scope is authorized. |
ERROR | SOLUTION |
BAD_REQUEST | Check the required parameters and data types. |
INVALID_REQUEST | Verify the format and structure of the request. |
FORBIDDEN | Verify that the user has the required permissions. |
TOO_MANY_REQUESTS | Implement the correct retry logic with exponential backoff. |
ERROR | SOLUTION |
NOT_FOUND | Verify that the project or company ID exists and is accessible. |
CONFLICT | Verify the state of the resource before initiating update operations. |
UNPROCESSABLE_ENTITY | Check the validation rules of the business logic. |
ERROR | SOLUTION |
CONNECTIVITY | Check the network connectivity and firewall settings. |
SERVICE_UNAVAILABLE | Procore service may be temporarily down. |
REQUEST_TIMEOUT | Increase the timeout settings. |
GATEWAY_TIMEOUT | Check the configuration of the proxy or load balancer. |
<logger name="com.procore.mule" level="DEBUG"/>
OPERATION | METHOD | ENDPOINT | DESCRIPTION |
Create Project | POST | /rest/v1.0/projects | Create a new project |
Update Project | PATCH | /rest/v1.0/projects/{id} | Update an existing project |
ERROR CODE | DESCRIPTION | CAUSE | SOLUTION |
BAD_REQUEST | Invalid request parameters or malformed data. | Missing required fields or invalid data types. | Check the request payload and validate parameters. |
UNAUTHORIZED | Invalid or expired authentication credentials. | Invalid OAuth tokens or expired access tokens. | Refresh the OAuth tokens or re-authenticate. |
FORBIDDEN | Insufficient permissions to access the resource. | The user lacks the required permissions. | Verify user permissions in Procore. |
NOT_FOUND | The requested resource was not found. | Invalid project or company ID or deleted resource. | Verify that the resource exists and is accessible. |
REQUEST_TIMEOUT | The request timed out. | Network latency or server overload. | Implement retry logic with backoff. |
CONFLICT | The request conflicts with the current state of the resource. | Concurrent modifications or business rule violations. | Check the resource state before updating. |
UNPROCESSABLE_ENTITY | Well-formed request with semantic errors. | Validation failures of the business logic. | Review business rules and data constraints. |
TOO_MANY_REQUESTS | Rate limit exceeded. | API rate limiting. | Implement exponential backoff retry. |
INTERNAL_SERVER_ERROR | Unexpected error on Procore server. | Server-side issues. | Contact Procore support. |
NOT_IMPLEMENTED | Requested operation is not supported. | API endpoint is unavailable. | Check the API documentation for supported operations. |
BAD_GATEWAY | Gateway error. | Proxy or load balancer issues. | Check the network infrastructure. |
SERVICE_UNAVAILABLE | Procore service is temporarily unavailable. | Maintenance or server issues. | Retry after the service is restored. |
GATEWAY_TIMEOUT | Gateway timeout. | Network timeout issues. | Increase the timeout settings. |
HTTP_VERSION_NOT_SUPPORTED | Unsupported HTTP version. | Protocol version mismatch. | Use a supported HTTP version. |
CONNECTIVITY | Unable to connect to Procore services. | Network issues or firewall blocking. | Check the network connectivity. |
INVALID_REQUEST | Request contains invalid parameters or format. | Malformed request structure. | Validate the request format. |
UNEXPECTED_ERROR | Unexpected error occurred. | Unknown or unclassified errors. | Check logs for detailed information on the error. |
TYPE | DESCRIPTION | EXAMPLE |
String | Text values | "Project Name" |
Boolean | True/false values | true |
Double | Decimal numbers | 1000000.0 |
Integer | Whole numbers | 50000 |
List | Array of strings | ["dept1", "dept2"] |
Date | Date values | "2024-01-01" |
TERM | DEFINITION |
Procore | Construction project management platform. |
OAuth 2.0 | Authorization protocol for secure API access. |
Company ID | Unique identifier for a Procore company. |
Project ID | Unique identifier for a Procore project. |
Standard Cost Codes | Predefined cost categories for construction projects. |
Custom Fields | User-defined fields for project customization. |
Procore Error Codes | Specific error types that are defined by the Procore connector for error handling. |
BAD_REQUEST | Error indicating invalid request parameters or malformed data. |
UNAUTHORIZED | Error indicating invalid or expired authentication credentials. |
FORBIDDEN | Error indicating insufficient permissions to access a resource. |
NOT_FOUND | Error indicating the requested resource was not found. |
CONFLICT | Error indicating request conflicts with current resource state. |
TOO_MANY_REQUESTS | Error indicating API rate limit has been exceeded. |
CONNECTIVITY | Error indicating inability to connect to Procore services. |
UNEXPECTED_ERROR | Generic error for unknown or unclassified errors. |