# Create IAM principal

> **POST** `https://api.us.nylas.com/v3/iam/principals`

Source: https://developer.nylas.com/docs/reference/api/iam-principals/create-iam-principal/

Requires `iam.principals.create` on an active principal bound to the authenticated organization. Authenticate with an active, unexpired IAM credential of type `api_key`. Application API keys (including legacy API keys) and provider OAuth tokens cannot authorize IAM management endpoints. Use an existing IAM credential with these permissions, or [create your first management credential in the Dashboard](/docs/v3/auth/nylas-iam/manage-principals-with-api/#create-a-management-credential-in-the-dashboard).

Creates a principal with exactly one resource binding. The organization comes from the caller credential; do not supply organization_id. Unknown fields and null values are rejected.

**Authentication:** IAM_API_KEY

## Request body

Content-Type: application/json

- `name` (string, minLength: 1, maxLength: 128) **(required)** - Descriptive workload name. Surrounding whitespace is removed.
- `description` (string, maxLength: 1024, default: `""`) - Optional workload description.
- `status` (string, one of: `active`, `disabled`, default: `"active"`) - Whether the principal or credential can authenticate.
- `roles` (array) - System or custom role IDs. Duplicate IDs are removed. Use the Dashboard to manage custom roles. Accepts up to 100 distinct IDs. Accepts up to 100 distinct IDs.
- `direct_permissions` (array) - Canonical permission IDs assigned directly to the principal. Duplicate IDs are removed. Accepts up to 100 distinct IDs. Accepts up to 100 distinct IDs.
- `resource_binding` (object) **(required)**
  - `type` (string, one of: `organization`, `application`, `workspace`, `grant`) **(required)** - Resource boundary for this principal.
  - `id` (string, minLength: 1, maxLength: 256) **(required)** - ID of a resource in the authenticated organization.

## Responses

### 200 - OK

- `request_id` (string) - Request ID for troubleshooting.
- `data` (object)
  - `id` (string) **(required)** - Opaque principal ID.
  - `organization_id` (string) **(required)** - Organization inferred from the authenticating IAM API key.
  - `name` (string, minLength: 1, maxLength: 128) **(required)** - Descriptive workload name. Surrounding whitespace is removed.
  - `description` (string, maxLength: 1024, default: `""`) **(required)** - Optional workload description.
  - `status` (string, one of: `active`, `disabled`, default: `"active"`) **(required)** - Whether the principal or credential can authenticate.
  - `roles` (array) **(required)** - System or custom role IDs. Duplicate IDs are removed. Use the Dashboard to manage custom roles. Accepts up to 100 distinct IDs. Accepts up to 100 distinct IDs.
  - `direct_permissions` (array) **(required)** - Canonical permission IDs assigned directly to the principal. Duplicate IDs are removed. Accepts up to 100 distinct IDs. Accepts up to 100 distinct IDs.
  - `resource_binding` (any) **(required)** - The single binding. May be null if its resource was removed and reconciliation cleared the binding.
  - `created_at` (integer, format: int64) **(required)** - Unix timestamp in seconds.
  - `updated_at` (integer, format: int64) **(required)** - Unix timestamp in seconds.

### 400 - Invalid request or object quota exceeded

- `request_id` (string) - Request ID for troubleshooting.
- `error` (object)
  - `code` (string) - Error code.
  - `type` (string) **(required)** - Error category.
  - `message` (string) **(required)** - Description of the failure.
  - `details` (object) - Additional error context. Quota errors include resource_type, limit, and current.
    - `resource_type` (string)
    - `limit` (integer)
    - `current` (integer)

### 401 - Invalid, disabled, deleted, or expired authentication credential

- `request_id` (string) - Request ID for troubleshooting.
- `error` (object)
  - `code` (string) - Error code.
  - `type` (string) **(required)** - Error category.
  - `message` (string) **(required)** - Description of the failure.
  - `details` (object) - Additional error context. Quota errors include resource_type, limit, and current.
    - `resource_type` (string)
    - `limit` (integer)
    - `current` (integer)

### 403 - Missing management permission or organization binding

- `request_id` (string) - Request ID for troubleshooting.
- `error` (object)
  - `code` (string) - Error code.
  - `type` (string) **(required)** - Error category.
  - `message` (string) **(required)** - Description of the failure.
  - `details` (object) - Additional error context. Quota errors include resource_type, limit, and current.
    - `resource_type` (string)
    - `limit` (integer)
    - `current` (integer)

### 404 - Principal or credential not found in this organization

- `request_id` (string) - Request ID for troubleshooting.
- `error` (object)
  - `code` (string) - Error code.
  - `type` (string) **(required)** - Error category.
  - `message` (string) **(required)** - Description of the failure.
  - `details` (object) - Additional error context. Quota errors include resource_type, limit, and current.
    - `resource_type` (string)
    - `limit` (integer)
    - `current` (integer)

### 409 - Conflicting principal or credential state

- `request_id` (string) - Request ID for troubleshooting.
- `error` (object)
  - `code` (string) - Error code.
  - `type` (string) **(required)** - Error category.
  - `message` (string) **(required)** - Description of the failure.
  - `details` (object) - Additional error context. Quota errors include resource_type, limit, and current.
    - `resource_type` (string)
    - `limit` (integer)
    - `current` (integer)

### 429 - Too many requests

- `request_id` (string) - Request ID for troubleshooting.
- `error` (object)
  - `code` (string) - Error code.
  - `type` (string) **(required)** - Error category.
  - `message` (string) **(required)** - Description of the failure.
  - `details` (object) - Additional error context. Quota errors include resource_type, limit, and current.
    - `resource_type` (string)
    - `limit` (integer)
    - `current` (integer)

## Code samples

### cURL

```bash
curl --request POST \
  --url "https://api.us.nylas.com/v3/iam/principals" \
  --header "Authorization: Bearer $NYLAS_IAM_ADMIN_KEY" \
  --header "Content-Type: application/json" \
  --data '{"name": "Support mailbox reader", "roles": ["role_system_messages_reader"], "resource_binding": {"type": "grant", "id": "<NYLAS_GRANT_ID>"}}'

```

### Node.js (HTTP)

```javascript
const requiredEnvironment = (name) => {
  const value = process.env[name]?.trim();
  if (!value) throw new Error(`Set ${name} before running this example`);
  return value;
};
const adminKey = requiredEnvironment("NYLAS_IAM_ADMIN_KEY");
// Node.js 22+: direct HTTP, without SDK-specific IAM methods.
const response = await fetch("https://api.us.nylas.com/v3/iam/principals", {
  method: "POST",
  signal: AbortSignal.timeout(30000),
  headers: {
    Authorization: `Bearer ${adminKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Support mailbox reader",
    roles: ["role_system_messages_reader"],
    resource_binding: {
      type: "grant",
      id: requiredEnvironment("NYLAS_GRANT_ID"),
    },
  }),
});
if (!response.ok)
  throw new Error(`Principal creation failed (${response.status})`);
const { data: principal } = await response.json();
console.log(principal.id);

```

### Python (HTTP)

```python
import json
import os
from urllib.request import Request, urlopen

request = Request(
    "https://api.us.nylas.com/v3/iam/principals",
    method="POST",
    headers={
        "Authorization": f"Bearer {os.environ['NYLAS_IAM_ADMIN_KEY']}",
        "Content-Type": "application/json",
    },
    data=json.dumps({
        "name": "Support mailbox reader",
        "roles": ["role_system_messages_reader"],
        "resource_binding": {"type": "grant", "id": os.environ["NYLAS_GRANT_ID"]},
    }).encode(),
)
with urlopen(request, timeout=30) as response:
    principal = json.load(response)["data"]
print(principal["id"])

```
