Summary and recommendation
The Outreach REST API follows JSON:API (jsonapi.org) spec throughout - every request and response uses `application/vnd.api+json` content negotiation, and standard JSON clients require adaptation before use. Authentication is OAuth 2.0 Authorization Code flow; access tokens expire after approximately 2 hours, so refresh_token rotation must be implemented.
A separate long-lived SCIM bearer token exists for SCIM 2.0 operations and is generated independently in the Outreach admin UI under Security settings - it is not interchangeable with OAuth tokens.
For identity graph construction, the canonical user identifier is the integer `id` field. Email (`email`) doubles as the login identifier and is filterable via `filter[email]=`. The `active` and `locked` boolean fields together represent provisioning state; deactivation is a PATCH setting `locked: true`, as hard deletion is not available via the REST API.
Role and profile assignments are relationship objects requiring valid IDs pre-fetched from `/roles` and `/profiles` before any create or update call.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (Authorization Code flow; also supports Client Credentials for server-to-server) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: OAuth 2.0 (Authorization Code flow; also supports Client Credentials for server-to-server)
Setup steps
- Register an OAuth application in the Outreach developer portal (https://developers.outreach.io)
- Obtain client_id and client_secret from the registered app
- Redirect user to https://api.outreach.io/oauth/authorize with response_type=code, client_id, redirect_uri, scope, and state params
- Exchange the returned authorization code for an access_token and refresh_token via POST to https://api.outreach.io/oauth/token
- Include the access_token as a Bearer token in the Authorization header on all API requests
- Refresh the access_token using the refresh_token before expiry (tokens expire in 2 hours)
Required scopes
| Scope | Description | Required for |
|---|---|---|
| users.read | Read user records | GET /users, GET /users/{id} |
| users.write | Create and update user records | POST /users, PATCH /users/{id} |
| users.delete | Delete or deactivate user records | DELETE /users/{id} |
| profiles.read | Read user profile/role data | GET /profiles |
| roles.read | Read role definitions | GET /roles |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique Outreach user identifier | auto-assigned | immutable | Used as path param in single-resource endpoints |
| firstName | string | User's first name | required | optional | |
| lastName | string | User's last name | required | optional | |
| string | Primary email address; used as login identifier | required | optional | Must be unique within the org | |
| username | string | Outreach username (often same as email) | optional | optional | |
| locked | boolean | Whether the user account is locked/deactivated | optional | optional | Set to true to deactivate; Outreach does not hard-delete users via REST |
| active | boolean | Whether the user is active in the org | optional | optional | Deprovisioning sets active=false |
| title | string | Job title | optional | optional | |
| phoneNumber | string | Primary phone number | optional | optional | |
| mobilePhoneNumber | string | Mobile phone number | optional | optional | |
| timeZone | string | IANA timezone string (e.g. America/New_York) | optional | optional | |
| locale | string | User locale (e.g. en-US) | optional | optional | |
| createdAt | datetime (ISO 8601) | Timestamp of user creation | auto-assigned | immutable | |
| updatedAt | datetime (ISO 8601) | Timestamp of last update | auto-assigned | auto-assigned | |
| role | relationship (Role) | Role assigned to the user | optional | optional | Specified as a JSON:API relationship object referencing a Role id |
| profile | relationship (Profile) | Profile (permissions set) assigned to the user | optional | optional | Specified as a JSON:API relationship object |
| externalSource | string | Source system identifier (e.g. SCIM) | optional | optional | Set automatically when provisioned via SCIM |
| externalId | string | External system user ID (e.g. IdP user ID) | optional | optional | Used for SCIM correlation |
Core endpoints
List Users
- Method: GET
- URL:
https://api.outreach.io/api/v2/users - Watch out for: Uses JSON:API format. Must include Accept: application/vnd.api+json header or requests may fail.
Request example
GET /api/v2/users?page[size]=50&page[offset]=0
Authorization: Bearer {access_token}
Content-Type: application/vnd.api+json
Response example
{
"data": [
{
"type": "user",
"id": "123",
"attributes": {
"firstName": "Jane",
"email": "jane@example.com",
"active": true
}
}
],
"meta": {"count": 200}
}
Get User
- Method: GET
- URL:
https://api.outreach.io/api/v2/users/{id} - Watch out for: id is an integer in the path but returned as a string in JSON:API responses.
Request example
GET /api/v2/users/123
Authorization: Bearer {access_token}
Response example
{
"data": {
"type": "user",
"id": "123",
"attributes": {
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"locked": false
}
}
}
Create User
- Method: POST
- URL:
https://api.outreach.io/api/v2/users - Watch out for: Creating a user does not automatically send an invitation email unless configured in org settings. Requires users.write scope.
Request example
POST /api/v2/users
Authorization: Bearer {access_token}
Content-Type: application/vnd.api+json
{"data":{"type":"user","attributes":{"firstName":"Jane","lastName":"Doe","email":"jane@example.com"}}}
Response example
{
"data": {
"type": "user",
"id": "456",
"attributes": {
"email": "jane@example.com",
"active": true,
"createdAt": "2024-01-15T10:00:00Z"
}
}
}
Update User
- Method: PATCH
- URL:
https://api.outreach.io/api/v2/users/{id} - Watch out for: PATCH is partial update (JSON:API compliant). The id must be included in the request body data object, not just the URL.
Request example
PATCH /api/v2/users/123
Authorization: Bearer {access_token}
Content-Type: application/vnd.api+json
{"data":{"type":"user","id":"123","attributes":{"title":"AE"}}}
Response example
{
"data": {
"type": "user",
"id": "123",
"attributes": {
"title": "AE",
"updatedAt": "2024-06-01T12:00:00Z"
}
}
}
Deactivate User (lock)
- Method: PATCH
- URL:
https://api.outreach.io/api/v2/users/{id} - Watch out for: Outreach does not support hard deletion of users via the REST API. Deactivation is done by setting locked=true.
Request example
PATCH /api/v2/users/123
Authorization: Bearer {access_token}
Content-Type: application/vnd.api+json
{"data":{"type":"user","id":"123","attributes":{"locked":true}}}
Response example
{
"data": {
"type": "user",
"id": "123",
"attributes": {"locked": true}
}
}
List Roles
- Method: GET
- URL:
https://api.outreach.io/api/v2/roles - Watch out for: Role IDs are needed when assigning roles during user create/update via relationship objects.
Request example
GET /api/v2/roles
Authorization: Bearer {access_token}
Response example
{
"data": [
{"type":"role","id":"1","attributes":{"name":"Admin"}},
{"type":"role","id":"2","attributes":{"name":"User"}}
]
}
List Profiles
- Method: GET
- URL:
https://api.outreach.io/api/v2/profiles - Watch out for: Profiles control feature-level permissions. Must be fetched separately to map names to IDs before assigning to users.
Request example
GET /api/v2/profiles
Authorization: Bearer {access_token}
Response example
{
"data": [
{"type":"profile","id":"10","attributes":{"name":"Sales Rep"}}
]
}
Filter Users by Email
- Method: GET
- URL:
https://api.outreach.io/api/v2/users?filter[email]={email} - Watch out for: Filter parameters follow JSON:API filter syntax. Not all fields are filterable; email and locked are supported.
Request example
GET /api/v2/users?filter[email]=jane@example.com
Authorization: Bearer {access_token}
Response example
{
"data": [
{"type":"user","id":"123","attributes":{"email":"jane@example.com"}}
]
}
Rate limits, pagination, and events
- Rate limits: Outreach enforces per-application rate limits on API requests. Limits are applied per OAuth application per org.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: Response headers include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. When limit is exceeded, API returns HTTP 429. Retry-After header is included on 429 responses.
- Pagination method: offset
- Default page size: 50
- Max page size: 1000
- Pagination pointer: page[size] and page[offset]
| Plan | Limit | Concurrent |
|---|---|---|
| Standard/Professional | 10,000 requests per hour | 0 |
| Enterprise | 10,000 requests per hour (higher limits available by request) | 0 |
- Webhooks available: Yes
- Webhook notes: Outreach supports webhooks (called 'Webhooks' in their platform) that can fire on object events. Webhook subscriptions can be created via the API or UI.
- Alternative event strategy: Poll GET /users with filter[updatedAt][gt]= for change detection if webhooks are not available on the plan.
- Webhook events: user.created, user.updated, user.deleted
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://api.outreach.io/scim/v2
Supported operations: GET /Users, GET /Users/{id}, POST /Users, PUT /Users/{id}, PATCH /Users/{id}, DELETE /Users/{id}, GET /Groups, POST /Groups, PATCH /Groups/{id}, DELETE /Groups/{id}
Limitations:
- Requires Enterprise plan with SSO enabled as a prerequisite
- SCIM and SSO must be configured as separate applications in the IdP (e.g., Okta)
- SCIM token is a long-lived bearer token generated in Outreach admin settings, not an OAuth token
- Group push maps to Outreach Teams, not Roles or Profiles
- Deprovisioning sets user to inactive; hard delete via SCIM DELETE may not permanently remove user data
- Google Workspace is not listed as a supported IdP for SCIM; Okta, Entra ID, and OneLogin are supported
Common scenarios
Provisioning a new rep requires four sequential calls: authenticate for an access token with users. write, `roles.
read, and profiles. readscopes; GET/api/v2/rolesto resolve the role ID; GET/api/v2/profilesto resolve the profile ID; then POST/api/v2/users` with the relationship objects included.
User creation does not trigger a welcome email by default - org settings must be configured separately to enable that behavior.
Deprovisioning starts with a lookup: GET /api/v2/users?filter[email]=departing@example.com to retrieve the integer user ID, then PATCH /api/v2/users/{id} with attributes: { locked: true }. Hard deletion is not available via REST; locked users may continue to occupy a licensed seat until removed by Outreach support or via SCIM DELETE on Enterprise. Sequence and prospect reassignment is not handled by the API deactivation call and must be addressed separately.
For SCIM automation via Okta, SSO and SCIM must be configured as two distinct Okta applications - a single combined app is explicitly unsupported and will cause provisioning failures. The SCIM base URL is https://api.outreach.io/scim/v2; group push maps to Outreach Teams, not to Roles or Profiles. Google Workspace is not listed as a supported SCIM IdP; Okta, Entra ID, and OneLogin are confirmed.
Provision a new sales rep via REST API
- Authenticate via OAuth 2.0 and obtain access_token with users.write, roles.read, profiles.read scopes
- GET /api/v2/roles to retrieve the 'User' role ID
- GET /api/v2/profiles to retrieve the target profile ID (e.g., 'Sales Rep')
- POST /api/v2/users with firstName, lastName, email, and relationship objects for role and profile
- Verify response returns HTTP 201 with the new user id and active=true
Watch out for: User creation does not automatically send a welcome/invite email unless org settings are configured to do so. Confirm with Outreach admin settings.
Deprovision a user when they leave the org
- GET /api/v2/users?filter[email]=departing@example.com to retrieve the user id
- PATCH /api/v2/users/{id} with attributes: {locked: true} to deactivate the account
- Optionally reassign the user's sequences/tasks to another rep via the Outreach UI or API before deactivation
Watch out for: Hard deletion is not available via REST API. Locked users still consume a license seat until fully removed by Outreach support or via SCIM DELETE on Enterprise.
Automate user provisioning via SCIM with Okta
- Confirm Enterprise plan is active and SSO is already configured in a separate Okta app
- In Outreach admin, navigate to Security > SCIM and generate a SCIM bearer token
- In Okta, create a new SCIM 2.0 app (separate from the SSO SAML app) with base URL https://api.outreach.io/scim/v2 and the bearer token
- Configure attribute mappings: userName→email, givenName→firstName, familyName→lastName, active→active
- Assign Okta groups to the SCIM app; group push maps to Outreach Teams
- Test provisioning by assigning a test user in Okta and verifying creation in Outreach
Watch out for: SCIM and SSO must be separate Okta applications. Combining them into one app is not supported and will cause provisioning failures.
Why building this yourself is a trap
The rate limit of 10,000 requests per hour is applied per OAuth application per org. At the default page size of 50, paginating a large user list burns requests quickly; always set page[size]=1000 for bulk reads.
The JSON:API filter syntax (filter[fieldName]=value) is not universal across fields - only email and locked are confirmed filterable on the users endpoint; attempting unsupported filters may return unfiltered results or errors without clear indication.
Relationship object IDs for roles and profiles are not stable across orgs and must be resolved at runtime - hardcoding them will cause failures in multi-org or re-provisioned environments. The id field is an integer in the URL path but is serialized as a string in JSON:API response bodies; type coercion bugs are common here.
Webhooks for user.created, user.updated, and user.deleted are available, but if webhooks are unavailable on the plan, poll GET /users with filter[updatedAt][gt]= as a fallback for change detection.
Automate Outreach 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.