Summary and recommendation
Drata exposes a REST API at https://public-api.drata.com, authenticated via Bearer token (API key). API access is gated to the Advanced plan and above; Foundation plan customers have no programmatic access. Keys are displayed only once at generation time and are scoped per workspace - a key from one workspace cannot query another.
The personnel object is the primary identity primitive. Key fields include `id` (UUID), `email`, `employmentType` (enum: FULL_TIME, CONTRACTOR, PART_TIME), `isActive` (boolean), `complianceStatus` (computed enum: COMPLIANT, NON_COMPLIANT, PENDING), `externalId` (for HRIS correlation), and `managerId` for org-graph traversal. The `complianceStatus` and `trainingStatus` fields are server-computed and cannot be written via API.
Pagination is offset-based using `page` / `limit` params (default 25, max 100, 1-based index).
For teams building an identity graph across their stack, `externalId` is the recommended join key between Drata personnel records and upstream HRIS or IdP sources. Mapping `managerId` chains enables org-hierarchy resolution within the compliance dataset.
Stitchflow's MCP server with 60+ deep IT/identity integrations can consume these fields to maintain a unified identity graph without custom pipeline work.
API quick reference
| Has user API | Yes |
| Auth method | API Key (Bearer token in Authorization header) |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Enterprise |
Authentication
Auth method: API Key (Bearer token in Authorization header)
Setup steps
- Log in to Drata as an Administrator.
- Navigate to Settings → API Keys.
- Click 'Generate API Key', assign a name and select desired scopes.
- Copy the generated key immediately (shown only once).
- Include the key in all requests as: Authorization: Bearer
Required scopes
| Scope | Description | Required for |
|---|---|---|
| personnel:read | Read personnel (user) records including profile and compliance status. | List and retrieve personnel/users |
| personnel:write | Create and update personnel records. | Creating or updating user records |
| controls:read | Read control data. | Fetching control assignments linked to users |
| evidence:read | Read evidence records. | Retrieving evidence associated with personnel |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string (UUID) | Unique Drata identifier for the personnel record. | system-generated | immutable | Use this ID for all subsequent operations. |
| string | Primary email address of the personnel. | required | updatable | Must be unique within the workspace. | |
| firstName | string | First name of the personnel. | required | updatable | |
| lastName | string | Last name of the personnel. | required | updatable | |
| title | string | Job title of the personnel. | optional | updatable | |
| department | string | Department the personnel belongs to. | optional | updatable | |
| employmentType | enum | Type of employment (e.g., FULL_TIME, CONTRACTOR, PART_TIME). | optional | updatable | Affects compliance task assignment logic. |
| startDate | string (ISO 8601 date) | Employment start date. | optional | updatable | |
| endDate | string (ISO 8601 date) | Employment end date; set when offboarding. | optional | updatable | Setting this value triggers offboarding workflows. |
| isActive | boolean | Whether the personnel record is active. | defaults to true | updatable | Deactivating removes user from compliance task queues. |
| role | enum | Drata platform role (e.g., ADMIN, MEMBER, AUDITOR). | optional | updatable | Controls access within the Drata platform. |
| managerId | string (UUID) | ID of the personnel's manager. | optional | updatable | References another personnel record's id. |
| complianceStatus | enum | Computed compliance status (e.g., COMPLIANT, NON_COMPLIANT, PENDING). | system-computed | read-only | Cannot be set directly via API. |
| trainingStatus | object | Summary of assigned training completion. | system-computed | read-only | |
| externalId | string | Identifier from an external HR or IdP system. | optional | updatable | Useful for correlating with HRIS or IdP records. |
Core endpoints
List Personnel
- Method: GET
- URL:
https://public-api.drata.com/public/personnel - Watch out for: Returns all personnel including inactive; filter with isActive=true if needed.
Request example
GET /public/personnel?page=1&limit=25
Authorization: Bearer <api_key>
Response example
{
"data": [
{"id":"uuid","email":"user@example.com","firstName":"Jane","lastName":"Doe","isActive":true}
],
"total": 120,
"page": 1,
"limit": 25
}
Get Personnel by ID
- Method: GET
- URL:
https://public-api.drata.com/public/personnel/{id} - Watch out for: Returns 404 if the personnel ID does not exist or belongs to a different workspace.
Request example
GET /public/personnel/uuid-1234
Authorization: Bearer <api_key>
Response example
{
"id": "uuid-1234",
"email": "user@example.com",
"firstName": "Jane",
"lastName": "Doe",
"complianceStatus": "COMPLIANT"
}
Create Personnel
- Method: POST
- URL:
https://public-api.drata.com/public/personnel - Watch out for: Duplicate email addresses return a 409 conflict. Compliance tasks are auto-assigned based on employmentType.
Request example
POST /public/personnel
Authorization: Bearer <api_key>
Content-Type: application/json
{
"email": "new@example.com",
"firstName": "John",
"lastName": "Smith",
"employmentType": "FULL_TIME"
}
Response example
{
"id": "uuid-5678",
"email": "new@example.com",
"firstName": "John",
"lastName": "Smith",
"isActive": true
}
Update Personnel
- Method: PATCH
- URL:
https://public-api.drata.com/public/personnel/{id} - Watch out for: Only include fields to be changed; omitted fields are not cleared.
Request example
PATCH /public/personnel/uuid-5678
Authorization: Bearer <api_key>
Content-Type: application/json
{
"title": "Senior Engineer",
"department": "Engineering"
}
Response example
{
"id": "uuid-5678",
"title": "Senior Engineer",
"department": "Engineering"
}
Deactivate Personnel
- Method: PATCH
- URL:
https://public-api.drata.com/public/personnel/{id} - Watch out for: Deactivation does not delete the record; historical compliance data is retained. Offboarding tasks are triggered by endDate.
Request example
PATCH /public/personnel/uuid-5678
Authorization: Bearer <api_key>
Content-Type: application/json
{
"isActive": false,
"endDate": "2025-06-01"
}
Response example
{
"id": "uuid-5678",
"isActive": false,
"endDate": "2025-06-01"
}
List Personnel Training Status
- Method: GET
- URL:
https://public-api.drata.com/public/personnel/{id}/training - Watch out for: Training records are read-only via API; completion must occur within Drata or integrated LMS.
Request example
GET /public/personnel/uuid-1234/training
Authorization: Bearer <api_key>
Response example
{
"personnelId": "uuid-1234",
"trainings": [
{"name":"Security Awareness","status":"COMPLETED","completedAt":"2025-01-15"}
]
}
List Personnel Background Checks
- Method: GET
- URL:
https://public-api.drata.com/public/personnel/{id}/background-checks - Watch out for: Background check initiation is not supported via API; only status retrieval is available.
Request example
GET /public/personnel/uuid-1234/background-checks
Authorization: Bearer <api_key>
Response example
{
"personnelId": "uuid-1234",
"backgroundChecks": [
{"status":"PASSED","completedAt":"2024-11-01"}
]
}
List Controls
- Method: GET
- URL:
https://public-api.drata.com/public/controls - Watch out for: ownerId references a personnel ID; useful for mapping control ownership to users.
Request example
GET /public/controls?page=1&limit=25
Authorization: Bearer <api_key>
Response example
{
"data": [
{"id":"ctrl-uuid","name":"Access Control","status":"PASSING","ownerId":"uuid-1234"}
],
"total": 80
}
Rate limits, pagination, and events
- Rate limits: Drata enforces rate limits on the public API; exact per-plan limits are not publicly documented in detail.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: HTTP 429 is returned when limits are exceeded. Retry-After header is included. Exact request-per-minute figures are not published in official docs as of this research.
- Pagination method: offset
- Default page size: 25
- Max page size: 100
- Pagination pointer: page / limit
| Plan | Limit | Concurrent |
|---|---|---|
| Advanced | API access enabled; specific rate limit not publicly documented | |
| Enterprise/Scale | Higher limits available; contact Drata for specifics |
- Webhooks available: No
- Webhook notes: Drata does not publicly document outbound webhook support for user/personnel events as of this research.
- Alternative event strategy: Poll the /public/personnel endpoint on a schedule to detect changes, or use Drata's native integrations (e.g., Okta SCIM) for real-time provisioning events.
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Enterprise
- Endpoint: Not documented
Limitations:
- SCIM provisioning is not natively offered by Drata as a SCIM provider endpoint.
- Drata acts as a SCIM consumer by connecting to IdPs (e.g., Okta) to ingest user data, not as a SCIM server.
- Enterprise plan and SSO configuration are prerequisites for IdP-based user sync.
- Automated provisioning into Drata from an IdP is handled via Okta integration, not a SCIM endpoint exposed by Drata.
Common scenarios
Onboarding: POST /public/personnel with email, firstName, lastName, employmentType, startDate, and externalId. Store the returned Drata id mapped to the HRIS record.
Drata auto-assigns compliance tasks based on employmentType. If the email already exists (e.
g. , from a prior IdP sync), the API returns HTTP 409 - resolve by PATCHing the existing record rather than retrying the POST.
Offboarding: PATCH /public/personnel/{id} with both isActive: false and endDate set to the last working day. Setting only isActive: false without endDate may not trigger all offboarding workflows. The record is retained for audit trail purposes; deletion is not supported via API.
Compliance status sync: GET /public/personnel?page=1&limit=100 and paginate using the total field. Extract id, email, complianceStatus, and trainingStatus per record and write to your data warehouse or dashboard. Schedule on a polling interval (hourly is a reasonable baseline) since Drata does not expose outbound webhooks for personnel events. Note that complianceStatus is computed asynchronously - newly created records may show PENDING for several minutes before all assigned controls are evaluated.
Onboard a new employee from HRIS
- POST /public/personnel with email, firstName, lastName, employmentType, startDate, and externalId (HRIS ID).
- Store the returned Drata personnel id mapped to the HRIS record.
- Drata auto-assigns compliance tasks (security training, background check acknowledgment) based on employmentType.
- Poll GET /public/personnel/{id}/training periodically to verify training completion.
Watch out for: If the email already exists in Drata (e.g., from a prior IdP sync), the POST returns 409. Resolve by PATCHing the existing record instead.
Offboard a departing employee
- PATCH /public/personnel/{id} with isActive: false and endDate set to the last working day.
- Verify the response confirms isActive is false.
- Drata will revoke pending compliance task assignments and flag the record as offboarded.
- Retain the record for audit trail purposes; do not attempt deletion.
Watch out for: Setting only isActive: false without endDate may not trigger all offboarding workflows; include both fields.
Sync personnel compliance status to an external dashboard
- GET /public/personnel?page=1&limit=100 and paginate through all pages using the total field.
- For each record, extract id, email, complianceStatus, and trainingStatus.
- Write results to your data warehouse or dashboard tool.
- Schedule this sync at a regular interval (e.g., hourly) since webhooks are not available.
Watch out for: complianceStatus is computed asynchronously; a newly created personnel record may show PENDING for several minutes before Drata evaluates all assigned controls.
Why building this yourself is a trap
The most significant API caveat is the plan gate: the public API is unavailable on the Foundation tier, meaning teams that start on the entry plan and build manual workflows cannot migrate to API-driven provisioning without an upgrade to Advanced (~$15,000/year) or higher. Budget this dependency explicitly.
Drata acts as a SCIM consumer (ingesting from IdPs like Okta), not a SCIM provider - it does not expose a SCIM endpoint for downstream systems to call. Automated provisioning into Drata from an IdP requires Enterprise tier plus SSO configuration; there is no SCIM server endpoint to target directly.
Rate limit specifics are not published in official documentation; implement exponential backoff on HTTP 429 responses and rely on the Retry-After header. The public API base URL (public-api.drata.com) is distinct from the app URL (app.drata.com) - mixing them in client configuration is a documented source of auth failures.
Automate Drata 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.