Stitchflow
QuickBooks logo

QuickBooks User Management API Guide

API workflow

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

UpdatedMar 16, 2026

Summary and recommendation

The QuickBooks Online Accounting API exposes an Employee entity at `https://quickbooks.api.intuit.com/v3/company/{realmId}/employee` under OAuth 2.0 with the `com.intuit.quickbooks.accounting` scope. This is a payroll/HR record object - it is not the same as a QuickBooks Online user login.

There is no API endpoint to invite users, assign roles, or revoke application access; those operations exist only in the QuickBooks UI and have no programmatic equivalent.

Authentication follows a standard OAuth 2.0 authorization code flow. Access tokens expire after 1 hour; refresh tokens expire after 100 days.

The `realmId` returned in the OAuth callback is required in every API URL and must be persisted at token-grant time.

For identity graph construction, the OpenID Connect userinfo endpoint (`https://accounts.platform.intuit.com/v1/openid_connect/userinfo`) returns the authenticated Intuit account's `sub`, `email`, `givenName`, and `familyName` - this is the only mechanism to resolve an OAuth actor to a real-world identity, and it requires the `openid`, `profile`, and `email` scopes in addition to the accounting scope.

API quick reference

Has user APIYes
Auth methodOAuth 2.0
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredN/A

Authentication

Auth method: OAuth 2.0

Setup steps

  1. Register an app at https://developer.intuit.com to obtain a Client ID and Client Secret.
  2. Direct the user to Intuit's authorization endpoint: https://appcenter.intuit.com/connect/oauth2 with response_type=code, client_id, redirect_uri, scope, and state parameters.
  3. Exchange the returned authorization code for access and refresh tokens via POST to https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer.
  4. Include the access token as a Bearer token in the Authorization header on all API requests.
  5. Refresh the access token (expires in 1 hour) using the refresh token (expires in 100 days) before expiry.

Required scopes

Scope Description Required for
com.intuit.quickbooks.accounting Full read/write access to QuickBooks Online accounting data including Employee entities. Reading and writing Employee records via the Accounting API.
com.intuit.quickbooks.payment Access to QuickBooks Payments data. Payment-related operations; not required for user/employee management.
openid OpenID Connect scope for identity; returns id_token with user profile. Retrieving the authenticated Intuit user's identity (sub, email, givenName, familyName).
profile Returns basic profile claims (name, phone number) in the userinfo endpoint. Fetching authenticated user profile via /oauth2/v1/openid_connect/userinfo.
email Returns the authenticated user's email address. Fetching email via /oauth2/v1/openid_connect/userinfo.

User object / data model

Field Type Description On create On update Notes
Id string Unique identifier for the Employee, assigned by QuickBooks. read-only required Must be included on update/delete.
SyncToken string Version token for optimistic locking; increments on each update. read-only required Must match current server value to avoid conflict errors.
GivenName string Employee's first name. Max 100 chars. optional optional
FamilyName string Employee's last name. Max 100 chars. optional optional
DisplayName string Name displayed in QuickBooks UI. Must be unique across all name-list objects. required optional Uniqueness enforced across Customer, Vendor, Employee, and Other Name lists.
PrintOnCheckName string Name printed on paychecks. optional optional
Active boolean Whether the employee is active. Defaults to true. optional optional Set to false to deactivate (soft delete).
PrimaryPhone object (TelephoneNumber) Primary phone number. Contains FreeFormNumber string. optional optional
PrimaryAddr object (PhysicalAddress) Primary address with Line1, City, CountrySubDivisionCode, PostalCode. optional optional
PrimaryEmailAddr object (EmailAddress) Primary email. Contains Address string. optional optional
SSN string Social Security Number (masked in responses). optional optional Returned masked (e.g., XXX-XX-1234) in read responses.
Gender string (enum) Male or Female. optional optional
BirthDate string (date) Date of birth in YYYY-MM-DD format. optional optional
HiredDate string (date) Date employee was hired. optional optional
ReleasedDate string (date) Date employee was released/terminated. optional optional
EmployeeType string (enum) Officer, Owner, Regular, Statutory. optional optional
Organization boolean True if the employee is an organization rather than an individual. optional optional
MetaData object (ModificationMetaData) Contains CreateTime and LastUpdatedTime timestamps. read-only read-only

Core endpoints

Create Employee

  • Method: POST
  • URL: https://quickbooks.api.intuit.com/v3/company/{realmId}/employee
  • Watch out for: DisplayName must be unique across all name-list objects (Customers, Vendors, Employees, Other Names). Duplicate DisplayName returns a 400 error.

Request example

POST /v3/company/123146/employee
Content-Type: application/json
Authorization: Bearer {access_token}

{"DisplayName":"Jane Smith","GivenName":"Jane","FamilyName":"Smith","PrimaryEmailAddr":{"Address":"jane@example.com"}}

Response example

{"Employee":{"Id":"55","SyncToken":"0","DisplayName":"Jane Smith","GivenName":"Jane","FamilyName":"Smith","Active":true,"MetaData":{"CreateTime":"2024-01-10T10:00:00","LastUpdatedTime":"2024-01-10T10:00:00"}}}

Read Employee (single)

  • Method: GET
  • URL: https://quickbooks.api.intuit.com/v3/company/{realmId}/employee/{employeeId}
  • Watch out for: SSN is always returned masked. There is no way to retrieve the full SSN via the API.

Request example

GET /v3/company/123146/employee/55
Authorization: Bearer {access_token}
Accept: application/json

Response example

{"Employee":{"Id":"55","SyncToken":"0","DisplayName":"Jane Smith","Active":true,"SSN":"XXX-XX-1234"}}

Query Employees (list)

  • Method: GET
  • URL: https://quickbooks.api.intuit.com/v3/company/{realmId}/query?query=SELECT * FROM Employee STARTPOSITION 1 MAXRESULTS 100
  • Watch out for: Use SQL-like syntax. MAXRESULTS cap is 1000. Filter inactive employees with WHERE Active IN (true, false).

Request example

GET /v3/company/123146/query?query=SELECT%20*%20FROM%20Employee%20STARTPOSITION%201%20MAXRESULTS%20100
Authorization: Bearer {access_token}

Response example

{"QueryResponse":{"Employee":[{"Id":"55","DisplayName":"Jane Smith","Active":true}],"startPosition":1,"maxResults":100,"totalCount":1}}

Update Employee (full)

  • Method: POST
  • URL: https://quickbooks.api.intuit.com/v3/company/{realmId}/employee
  • Watch out for: QuickBooks Online Accounting API uses POST (not PUT/PATCH) for updates. Full object must be sent; partial updates are not supported. SyncToken must be current or the request fails with a 400 conflict.

Request example

POST /v3/company/123146/employee
Content-Type: application/json

{"Id":"55","SyncToken":"0","DisplayName":"Jane Smith","GivenName":"Jane","FamilyName":"Smith-Updated","Active":true}

Response example

{"Employee":{"Id":"55","SyncToken":"1","DisplayName":"Jane Smith","FamilyName":"Smith-Updated"}}

Deactivate Employee (soft delete)

  • Method: POST
  • URL: https://quickbooks.api.intuit.com/v3/company/{realmId}/employee
  • Watch out for: Hard delete is not supported for Employee objects. Setting Active=false is the only deactivation mechanism. Inactive employees still appear in queries when Active IN (true, false) is used.

Request example

POST /v3/company/123146/employee
Content-Type: application/json

{"Id":"55","SyncToken":"1","DisplayName":"Jane Smith","Active":false}

Response example

{"Employee":{"Id":"55","SyncToken":"2","DisplayName":"Jane Smith","Active":false}}

Get Authenticated User Info (OpenID Connect)

  • Method: GET
  • URL: https://accounts.platform.intuit.com/v1/openid_connect/userinfo
  • Watch out for: This returns the Intuit account identity of the OAuth-authenticated user, not a QuickBooks company employee. Requires openid + profile + email scopes.

Request example

GET /v1/openid_connect/userinfo
Authorization: Bearer {access_token}

Response example

{"sub":"1234567890","email":"user@example.com","givenName":"John","familyName":"Doe","phoneNumber":"+15551234567"}

Read Employee (sparse update check)

  • Method: GET
  • URL: https://quickbooks.api.intuit.com/v3/company/{realmId}/employee/{employeeId}?minorversion=65
  • Watch out for: Always fetch the latest SyncToken before any update to avoid optimistic-lock conflicts. Minor version parameter affects available fields; use the latest supported minor version.

Request example

GET /v3/company/123146/employee/55?minorversion=65
Authorization: Bearer {access_token}

Response example

{"Employee":{"Id":"55","SyncToken":"2","DisplayName":"Jane Smith","Active":false,"MetaData":{"LastUpdatedTime":"2024-01-11T09:00:00"}}}

Change Data Capture (CDC) – detect employee changes

  • Method: GET
  • URL: https://quickbooks.api.intuit.com/v3/company/{realmId}/cdc?entities=Employee&changedSince={ISO8601timestamp}
  • Watch out for: CDC is the recommended polling mechanism for sync; changedSince window is limited to 30 days. Does not replace webhooks but is the primary change-detection pattern for employees.

Request example

GET /v3/company/123146/cdc?entities=Employee&changedSince=2024-01-01T00:00:00Z
Authorization: Bearer {access_token}

Response example

{"CDCResponse":[{"QueryResponse":[{"Employee":[{"Id":"55","SyncToken":"2","Active":false}],"startPosition":1,"maxResults":1}]}]}

Rate limits, pagination, and events

  • Rate limits: Intuit enforces throttling at the app and company (realmId) level. Official docs state a limit of 500 requests per minute per realmId for the Accounting API. Exceeding limits returns HTTP 429.
  • Rate-limit headers: No
  • Retry-After header: No
  • Rate-limit notes: Official docs do not explicitly document rate-limit response headers or a Retry-After header. When throttled, Intuit returns HTTP 429 with an error body. Implement exponential backoff. Minor version header (Intuit-Tid) is present for tracing but is not a rate-limit header.
  • Pagination method: offset
  • Default page size: 100
  • Max page size: 1000
  • Pagination pointer: startposition / maxresults (SQL-like query string)
Plan Limit Concurrent
All QuickBooks Online plans (app-level) 500 requests/minute per realmId 0
  • Webhooks available: Yes
  • Webhook notes: Intuit provides a Webhooks service for QuickBooks Online that delivers event notifications to a registered HTTPS endpoint when entities (including Employee) are created, updated, or deleted. Webhooks must be configured in the Intuit Developer Portal under the app's settings.
  • Alternative event strategy: Change Data Capture (CDC) API endpoint (/v3/company/{realmId}/cdc) can be polled to detect entity changes when webhooks are not feasible. CDC window is limited to 30 days.
  • Webhook events: Employee.Create, Employee.Update, Employee.Delete, Customer.Create, Customer.Update, Vendor.Create, Vendor.Update

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: N/A
  • Endpoint: Not documented

Limitations:

  • QuickBooks Online does not offer a native SCIM 2.0 endpoint for any plan tier.
  • No SSO/SAML support is available on QuickBooks Online (any plan).
  • No IdP (Okta, Entra ID, Google Workspace, OneLogin) native SCIM connector is officially supported.
  • User provisioning must be performed via the Accounting API Employee entity or manually in the QuickBooks UI.

Common scenarios

Three integration patterns are well-supported by the API:

  • Provision on hire: POST to /employee with DisplayName (must be globally unique across Customers, Vendors, Employees, and Other Names in the company), GivenName, FamilyName, HiredDate, and PrimaryEmailAddr. Store the returned Id and SyncToken immediately. Pre-check DisplayName uniqueness with a query before posting to avoid HTTP 400 collisions.

  • Roster sync to external HR system: On initial load, paginate with SELECT * FROM Employee WHERE Active IN (true, false) STARTPOSITION 1 MAXRESULTS 1000. On subsequent runs, use Change Data Capture: GET /cdc?entities=Employee&changedSince={ISO8601timestamp}. CDC is capped at a 30-day window - if the sync job lapses beyond that, fall back to a full query. Store the sync timestamp after a successful CDC response, not before.

  • Deactivate on termination: GET the employee to retrieve the current SyncToken, set Active=false and ReleasedDate to the termination date, then POST the full object back. Hard delete is not supported. Critically, this does not revoke the employee's QuickBooks Online user login if one exists - that step requires a separate manual action in the UI.

Provision a new employee record on hire

  1. Obtain a valid OAuth 2.0 access token with scope com.intuit.quickbooks.accounting.
  2. POST to /v3/company/{realmId}/employee with DisplayName (unique), GivenName, FamilyName, HiredDate, PrimaryEmailAddr, and EmployeeType.
  3. Store the returned Id and SyncToken for future updates.
  4. Optionally register a webhook for Employee.Create events to confirm creation in downstream systems.

Watch out for: DisplayName uniqueness is enforced globally across all name-list objects. Pre-check with a query (SELECT * FROM Employee WHERE DisplayName = 'X') before creating to avoid 400 errors.

Sync employee roster to an external HR system

  1. On initial sync, query all employees: GET /v3/company/{realmId}/query?query=SELECT * FROM Employee WHERE Active IN (true, false) STARTPOSITION 1 MAXRESULTS 1000.
  2. Paginate using STARTPOSITION until totalCount is exhausted.
  3. Store each employee's Id, SyncToken, and MetaData.LastUpdatedTime.
  4. On subsequent syncs, use CDC: GET /v3/company/{realmId}/cdc?entities=Employee&changedSince={lastSyncTimestamp} to retrieve only changed records.
  5. Apply creates, updates, and deletes (Active=false) to the external system accordingly.

Watch out for: CDC changedSince is limited to 30 days. If the sync job has not run in over 30 days, fall back to a full query. Store the sync timestamp immediately after a successful CDC response, not before.

Deactivate an employee on termination

  1. GET /v3/company/{realmId}/employee/{employeeId} to retrieve the current SyncToken and full object.
  2. Set ReleasedDate to the termination date in the retrieved object.
  3. Set Active to false.
  4. POST the full modified object back to /v3/company/{realmId}/employee.
  5. Confirm the response returns SyncToken incremented by 1 and Active=false.

Watch out for: Hard delete is not available. Active=false is the only supported deactivation. The employee record remains queryable with WHERE Active IN (true, false). QuickBooks Online user login access (if the employee also had a QBO user account) must be revoked separately via the QuickBooks UI - there is no API for this.

Why building this yourself is a trap

The most consequential caveat for any identity graph or joiner-mover-leaver automation: the Employee API and the User Login system are entirely separate. An employee record (API-managed) and a QBO user account (UI-only) can exist independently, refer to the same person, or diverge silently. Setting Active=false on an Employee record does not touch login access.

There is no API to query which employees have active user accounts, what roles they hold, or when they last signed in.

All updates use POST - not PUT or PATCH - and require the full object. SyncToken must match the server's current value on every write; always GET before POST to avoid optimistic-lock conflicts.

The rate limit is 500 requests per minute per realmId; Intuit does not return rate-limit headers or a Retry-After value on HTTP 429 responses, so implement exponential backoff unconditionally.

Webhooks for Employee.Create, Employee.Update, and Employee.Delete are available but must be registered in the Intuit Developer Portal - CDC polling remains the more reliable change-detection fallback given webhook delivery is not guaranteed.

Automate QuickBooks 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 16, 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