Stitchflow
Procore logo

Procore User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 9, 2026

Summary and recommendation

Procore's REST API (base URL: `https://api.procore.com/rest/v1.0`) uses OAuth 2.0 for authentication, supporting both Authorization Code and Client Credentials grant types. All requests require a `company_id` or `project_id` as a path parameter-there is no global user namespace. User payloads must be nested under a `user` key (e.g., `{"user": {...}}`); flat objects will be rejected.

There is no native SCIM 2.0 endpoint; all automated lifecycle management must be implemented against the REST API directly.

For teams building identity graph integrations, Procore's two-tier model (company directory + per-project membership) means a complete identity graph entry for any user requires data from both the company users endpoint and each relevant project users endpoint-these are not unified in a single response.

API quick reference

Has user APIYes
Auth methodOAuth 2.0
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredCustom (ACV-based)

Authentication

Auth method: OAuth 2.0

Setup steps

  1. Register a developer application at https://developers.procore.com/apps to obtain a client_id and client_secret.
  2. Choose an OAuth 2.0 grant type: Authorization Code (user-facing apps) or Client Credentials (service/machine-to-machine apps).
  3. For Authorization Code: redirect the user to https://login.procore.com/oauth/authorize with client_id, redirect_uri, response_type=code, and desired scopes.
  4. Exchange the authorization code for an access token via POST to https://login.procore.com/oauth/token.
  5. For Client Credentials: POST directly to https://login.procore.com/oauth/token with grant_type=client_credentials, client_id, and client_secret.
  6. Include the access token in all API requests as a Bearer token in the Authorization header.
  7. Access tokens expire; use the refresh_token (Authorization Code flow) or re-request (Client Credentials) to obtain a new token.

Required scopes

Scope Description Required for
read Read access to Procore resources including users and directory data. GET operations on users, company directory, project users
write Write access to create and update Procore resources including users. POST, PATCH, PUT, DELETE operations on users

User object / data model

Field Type Description On create On update Notes
id integer Unique Procore user ID. system-generated read-only Used as path parameter for user-specific operations.
login string User's email address used as login credential. required optional Must be unique across Procore. Acts as the primary identifier for login.
name string Full display name of the user. required optional Combination of first_name and last_name in some contexts.
first_name string User's first name. required optional
last_name string User's last name. required optional
email_address string Primary email address. required optional Typically same as login.
job_title string User's job title. optional optional
business_phone string Business phone number. optional optional
mobile_phone string Mobile phone number. optional optional
is_active boolean Whether the user account is active. optional (defaults true) optional Set to false to deactivate without deleting.
is_employee boolean Indicates if the user is an employee of the company. optional optional
company_id integer ID of the company the user belongs to. required (path param) read-only Passed as query/path parameter, not in body.
vendor_id integer ID of the vendor/company associated with the user. optional optional Links user to a vendor record in the directory.
permission_template_id integer ID of the permission template to apply to the user. optional optional Determines role-based access within projects.
created_at datetime Timestamp when the user was created. system-generated read-only ISO 8601 format.
updated_at datetime Timestamp of last update. system-generated system-generated ISO 8601 format.
avatar string (URL) URL to the user's profile avatar image. optional optional
address string Street address. optional optional
city string City. optional optional
country_code string ISO country code. optional optional e.g., US, CA, GB.

Core endpoints

List Company Users

  • Method: GET
  • URL: https://api.procore.com/rest/v1.0/companies/{company_id}/users
  • Watch out for: Returns users at the company level only. Project-level user membership requires a separate endpoint. Pagination defaults to 100 per page; use per_page up to 10000.

Request example

GET /rest/v1.0/companies/12345/users?page=1&per_page=100
Authorization: Bearer {access_token}

Response example

[
  {
    "id": 9876,
    "login": "jane.doe@example.com",
    "name": "Jane Doe",
    "is_active": true,
    "created_at": "2024-01-15T10:00:00Z"
  }
]

Show Company User

  • Method: GET
  • URL: https://api.procore.com/rest/v1.0/companies/{company_id}/users/{id}
  • Watch out for: Requires the exact Procore user ID (integer). Email-based lookup is not supported directly; use the list endpoint with filters.

Request example

GET /rest/v1.0/companies/12345/users/9876
Authorization: Bearer {access_token}

Response example

{
  "id": 9876,
  "login": "jane.doe@example.com",
  "first_name": "Jane",
  "last_name": "Doe",
  "is_active": true,
  "job_title": "Project Manager"
}

Create Company User

  • Method: POST
  • URL: https://api.procore.com/rest/v1.0/companies/{company_id}/users
  • Watch out for: The user body must be nested under a 'user' key. Creating a user at the company level does not automatically add them to any project; project membership is a separate operation.

Request example

POST /rest/v1.0/companies/12345/users
Authorization: Bearer {access_token}
Content-Type: application/json

{"user":{"login":"john.smith@example.com","first_name":"John","last_name":"Smith","is_employee":true}}

Response example

{
  "id": 9877,
  "login": "john.smith@example.com",
  "first_name": "John",
  "last_name": "Smith",
  "is_active": true,
  "created_at": "2025-06-01T12:00:00Z"
}

Update Company User

  • Method: PATCH
  • URL: https://api.procore.com/rest/v1.0/companies/{company_id}/users/{id}
  • Watch out for: Partial updates are supported. Setting is_active=false deactivates the user but does not remove them from projects. Full deletion is a separate operation.

Request example

PATCH /rest/v1.0/companies/12345/users/9876
Authorization: Bearer {access_token}
Content-Type: application/json

{"user":{"job_title":"Senior PM","is_active":false}}

Response example

{
  "id": 9876,
  "login": "jane.doe@example.com",
  "job_title": "Senior PM",
  "is_active": false,
  "updated_at": "2025-06-01T13:00:00Z"
}

Delete Company User

  • Method: DELETE
  • URL: https://api.procore.com/rest/v1.0/companies/{company_id}/users/{id}
  • Watch out for: Deletion may be blocked if the user has associated records (RFIs, submittals, etc.). Procore recommends deactivating (is_active=false) rather than deleting to preserve audit history.

Request example

DELETE /rest/v1.0/companies/12345/users/9876
Authorization: Bearer {access_token}

Response example

HTTP 204 No Content

List Project Users

  • Method: GET
  • URL: https://api.procore.com/rest/v1.0/projects/{project_id}/users
  • Watch out for: Project-level user list is scoped to users added to that specific project. A company user must be explicitly added to a project to appear here.

Request example

GET /rest/v1.0/projects/55555/users?page=1&per_page=100
Authorization: Bearer {access_token}

Response example

[
  {
    "id": 9876,
    "login": "jane.doe@example.com",
    "name": "Jane Doe",
    "permission_template_id": 101
  }
]

Add User to Project

  • Method: POST
  • URL: https://api.procore.com/rest/v1.0/projects/{project_id}/users
  • Watch out for: The user must already exist at the company level before being added to a project. permission_template_id controls project-level access; omitting it may result in no-access assignment.

Request example

POST /rest/v1.0/projects/55555/users
Authorization: Bearer {access_token}
Content-Type: application/json

{"user":{"id":9876,"permission_template_id":101}}

Response example

{
  "id": 9876,
  "name": "Jane Doe",
  "permission_template_id": 101
}

Remove User from Project

  • Method: DELETE
  • URL: https://api.procore.com/rest/v1.0/projects/{project_id}/users/{id}
  • Watch out for: Removes project membership only; the user remains in the company directory. Does not delete any project data created by the user.

Request example

DELETE /rest/v1.0/projects/55555/users/9876
Authorization: Bearer {access_token}

Response example

HTTP 204 No Content

Rate limits, pagination, and events

  • Rate limits: Procore enforces rate limits per OAuth application (client_id). Limits apply on a per-minute rolling window basis.
  • Rate-limit headers: Yes
  • Retry-After header: Yes
  • Rate-limit notes: When the rate limit is exceeded, the API returns HTTP 429. Response headers include X-Rate-Limit-Limit, X-Rate-Limit-Remaining, and X-Rate-Limit-Reset. Retry-After header is included on 429 responses. Limits are per OAuth application client_id, not per end user.
  • Pagination method: offset
  • Default page size: 100
  • Max page size: 10000
  • Pagination pointer: page and per_page
Plan Limit Concurrent
Standard (all plans) 3,600 requests per hour (approximately 60 requests/minute) per OAuth app 0
  • Webhooks available: Yes
  • Webhook notes: Procore supports webhooks that fire on resource events. Webhooks are configured per company and can target HTTP endpoints. Configuration is available via the API or the Procore Developer Portal.
  • Alternative event strategy: If webhooks are not suitable, poll the List Company Users endpoint with updated_after filter parameter to detect changes.
  • Webhook events: create (user created), update (user updated), delete (user deleted), create (project user added), delete (project user removed)

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Custom (ACV-based)
  • Endpoint: Not documented

Limitations:

  • Procore does not offer a native SCIM 2.0 endpoint as of the current documentation.
  • User provisioning via Okta or Entra ID SSO is supported for authentication but not automated SCIM provisioning/deprovisioning.
  • Users must be created manually in the Procore Directory tool or via the REST API.
  • No automated lifecycle management (deprovisioning) through IdP SCIM push is available natively.

Common scenarios

Three scenarios cover the majority of lifecycle automation use cases. To provision a new employee, POST to `/rest/v1.

0/companies/{company_id}/userswithis_employee=true, capture the returned id, then POST to /rest/v1. 0/projects/{project_id}/userswith thatidand a validpermission_template_id`-omitting the second call leaves the user in the directory with zero project access.

To deactivate a departed employee, PATCH /rest/v1. 0/companies/{company_id}/users/{id} with {"user": {"is_active": false}}; this blocks login but does not remove the user from project member lists-project removal must be executed per-project via DELETE `/rest/v1.

0/projects/{project_id}/users/{id}. For bulk directory sync, GET /rest/v1.

0/companies/{company_id}/users? per_page=10000retrieves up to 10,000 records per page; use theX-Totalresponse header to determine whether pagination is needed, and useupdated_after` for incremental syncs where the endpoint version supports it.

Provision a new employee and add them to a project

  1. POST /rest/v1.0/companies/{company_id}/users with first_name, last_name, login (email), is_employee=true to create the company-level user.
  2. Capture the returned user id from the response.
  3. POST /rest/v1.0/projects/{project_id}/users with the user id and the appropriate permission_template_id to grant project access.
  4. Verify membership with GET /rest/v1.0/projects/{project_id}/users/{id}.

Watch out for: Skipping the project membership step leaves the user in the company directory with no project access. permission_template_id must be a valid template ID for that project.

Deactivate a departed employee across the company

  1. GET /rest/v1.0/companies/{company_id}/users?search={email} to locate the user and retrieve their id.
  2. PATCH /rest/v1.0/companies/{company_id}/users/{id} with {"user": {"is_active": false}} to deactivate the account.
  3. Optionally, DELETE /rest/v1.0/projects/{project_id}/users/{id} for each project to remove active project memberships if required.
  4. Verify deactivation with GET /rest/v1.0/companies/{company_id}/users/{id} and confirm is_active=false.

Watch out for: Deactivation prevents login but does not remove the user from project member lists automatically. Project removal must be done per-project. Avoid hard deletion to preserve audit trail.

Sync company users to an external directory (bulk read)

  1. GET /rest/v1.0/companies/{company_id}/users?page=1&per_page=10000 to retrieve all users in a single page (if under 10,000).
  2. If total users exceed 10,000 (check X-Total header), iterate pages incrementally using the page parameter.
  3. For incremental syncs, use the updated_after query parameter (if supported on the endpoint) or compare updated_at timestamps to detect changes.
  4. Map Procore user fields (login, first_name, last_name, is_active) to the target directory schema.

Watch out for: The updated_after filter availability varies by endpoint version; verify in the API reference. X-Total header provides total record count for pagination planning. Large exports near the 10,000 per_page limit may approach rate limit thresholds.

Why building this yourself is a trap

The most common integration failure point is treating company-level user creation as equivalent to access provisioning-it is not. A user created at the company level has no project visibility until explicitly added to each project with a permission_template_id.

Deletion is a second trap: DELETE on a user with associated records (RFIs, submittals, daily logs) may fail silently or leave orphaned references; prefer is_active=false via PATCH to preserve audit history.

Rate limits are enforced at 3,600 requests per hour per OAuth client_id; bulk operations must monitor X-Rate-Limit-Remaining headers and implement exponential backoff on HTTP 429 responses. Finally, email-based lookup is not a dedicated filter-the search query parameter returns partial matches, so downstream logic must validate exact login field matches before acting on results.

Automate Procore workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 9, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste