# Manage IAM principals with the public API

Source: https://developer.nylas.com/docs/v3/auth/nylas-iam/manage-principals-with-api/

Create a principal that can read one mailbox, issue its credential, and rotate that credential through the IAM API. For principal, permission, and resource-binding concepts, see [Nylas IAM](/docs/v3/auth/nylas-iam/).

## Before you begin

IAM management endpoints require an **IAM credential** whose active principal has an **Organization** resource binding and the required management permissions. Existing application API keys (including legacy API keys) and provider OAuth tokens can't authorize these endpoints.

If you already have an active, unexpired IAM credential with these permissions, you can use it. Otherwise, create your first management credential in the Dashboard before making API requests.

### Create a management credential in the Dashboard

In the [Nylas Dashboard](https://dashboard.nylas.com/):

1. Open your organization's **Nylas IAM** page and create a principal with an **Organization** resource binding.
2. Assign the **IAM Admin** system role (`role_system_iam_admin`).
3. Open the **Credentials** tab and create a credential for that active principal. Choose an expiration and store the secret securely.
4. Load the secret into your server as `NYLAS_IAM_ADMIN_KEY`. Keep it separate from the scoped key you create for your workload.

Send the credential's secret as `Authorization: Bearer <NYLAS_IAM_ADMIN_KEY>`. Requests act within that credential's organization.

Use `https://api.us.nylas.com` for the U.S. region or `https://api.eu.nylas.com` for the E.U. region. Use the same region as the application that owns a Grant, Workspace, or Application binding.

> **Warn:** 
> **An IAM management key can change other principals' access.** Keep it in your provisioning server. Give each worker or agent a separate key with only its runtime permissions.

### Choose management permissions

The **IAM Admin** role lets you manage principals and credentials. For read-only access, use **IAM Auditor** (`role_system_iam_auditor`). Both require an Organization resource binding.

For narrower automation, assign only the permissions you need through a custom role or direct assignments. Each operation in the [principals](/docs/reference/api/iam-principals/) and [credentials](/docs/reference/api/iam-credentials/) references lists its required permission. Manage custom roles in the Dashboard.

## Provision a workload

Use your management credential for both creation requests. The workload will use the separate credential you create for it.

### Create a scoped principal

This example creates a mailbox reader bound to one grant. Set `NYLAS_GRANT_ID` to a grant in your organization. In the curl request, replace `<NYLAS_GRANT_ID>` in the JSON body with that ID.

```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>"}}'


```

```js [createIamPrincipal-Node.js HTTP]

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 [createIamPrincipal-Python HTTP]

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"])


```

Save the returned `data.id` as `NYLAS_PRINCIPAL_ID`. This principal has `messages.read` permission for the bound grant. For other bindings, roles, and field requirements, see [Create IAM principal](/docs/reference/api/iam-principals/create-iam-principal/).

<a id="create-an-iam-api-key"></a>

### Create an IAM credential

Set `NYLAS_PRINCIPAL_ID` to the principal ID returned above. Create an `api_key` credential with a 90-day lifetime:

```bash
# Store the returned secret securely. Do not capture it in shared logs.
EXPIRES_AT=$(python3 -c "import time; print(int(time.time()) + 90 * 86400)")

curl --request POST \
  --url "https://api.us.nylas.com/v3/iam/principals/${NYLAS_PRINCIPAL_ID}/credentials" \
  --header "Authorization: Bearer $NYLAS_IAM_ADMIN_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"name\": \"Support worker key\", \"expires_at\": ${EXPIRES_AT}}"


```

```js [createIamCredential-Node.js HTTP]

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");
// Store data.secret in your secrets service. Never log the response.
const principalId = encodeURIComponent(
  requiredEnvironment("NYLAS_PRINCIPAL_ID"),
);
const response = await fetch(
  `https://api.us.nylas.com/v3/iam/principals/${principalId}/credentials`,
  {
    method: "POST",
    signal: AbortSignal.timeout(30000),
    headers: {
      Authorization: `Bearer ${adminKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Support worker key",
      expires_at: Math.floor(Date.now() / 1000) + 90 * 86400,
    }),
  },
);
if (!response.ok)
  throw new Error(`Credential creation failed (${response.status})`);
const { data: credential } = await response.json();
// Pass credential.secret directly to your secrets-service client here.
// Save credential.id so you can rotate or revoke this key later.


```

```python [createIamCredential-Python HTTP]

import json
import os
import time
from urllib.parse import quote
from urllib.request import Request, urlopen

principal_id = quote(os.environ["NYLAS_PRINCIPAL_ID"], safe="")
request = Request(
    f"https://api.us.nylas.com/v3/iam/principals/{principal_id}/credentials",
    method="POST",
    headers={
        "Authorization": f"Bearer {os.environ['NYLAS_IAM_ADMIN_KEY']}",
        "Content-Type": "application/json",
    },
    data=json.dumps({
        "name": "Support worker key",
        "expires_at": int(time.time()) + 90 * 86400,
    }).encode(),
)
with urlopen(request, timeout=30) as response:
    credential = json.load(response)["data"]
# Pass credential["secret"] directly to your secrets-service client here.
# Save credential["id"] for rotation or revocation. Never log the response.


```

Store `data.secret` in your secrets service immediately. Nylas returns it only once, so don't log the response. Save `data.id` for rotation or revocation.

The credential inherits its principal's access. For expiration requirements and response fields, see [Create IAM credential](/docs/reference/api/iam-credentials/create-iam-credential/).

<a id="use-the-scoped-key"></a>

### Use the workload credential

Load the new secret as `NYLAS_IAM_API_KEY` in the workload's server. It can now read messages from the bound grant:

```bash
curl --request GET \
  --url "https://api.us.nylas.com/v3/grants/$NYLAS_GRANT_ID/messages" \
  --header "Authorization: Bearer $NYLAS_IAM_API_KEY"
```

Keep the organization management key out of this workload. Provider OAuth scopes still constrain what Nylas can do with the connected account; IAM permissions don't replace them.

## Manage existing principals and credentials

Use the organization management credential for these operations. Changes to a principal affect all credentials attached to it.

### List and paginate principals or credentials

Use [List IAM principals](/docs/reference/api/iam-principals/list-iam-principals/) or [List IAM credentials](/docs/reference/api/iam-credentials/list-iam-credentials/). If the response includes `next_cursor`, pass it as `page_token` on the next request. Stop when `next_cursor` is absent. The references cover filters and page sizes.

### Update a principal's access

Use [Update IAM principal](/docs/reference/api/iam-principals/update-iam-principal/) to change the workload's access. Supplied fields replace their current values; omitted fields stay unchanged. For example, `{"direct_permissions": ["messages.read"]}` replaces the entire direct-permission set.

Disable a principal to stop all of its credentials from authenticating. Delete it only when you want to permanently remove the principal and every attached credential. See [Delete IAM principal](/docs/reference/api/iam-principals/delete-iam-principal/).

### Rotate or revoke credentials

To rotate a key without downtime:

1. Create a replacement credential on the same principal.
2. Store and deploy the new secret.
3. Verify the workload can make its intended requests.
4. [Delete the old credential](/docs/reference/api/iam-credentials/delete-iam-credential/).

To stop access immediately, [disable the credential](/docs/reference/api/iam-credentials/update-iam-credential/). Deleting a credential is permanent.

## Understand principal and credential limits

Reserve credential capacity for rotation so you can deploy a replacement before deleting the old credential. For quota formulas, counting rules, and examples, see the [IAM principals reference](/docs/reference/api/iam-principals/).

## Troubleshoot IAM management requests

For authentication or permission failures, check that your management credential is active and unexpired, its principal is active, and the principal has an Organization binding and the operation's required permission. Use the response's `request_id` to inspect [IAM activity](/docs/v3/auth/nylas-iam/#review-and-troubleshoot-iam-activity). Each operation's reference lists its errors.

If credential creation times out, list the principal's credentials before retrying. Nylas might have created a credential even if you didn't receive its secret. Delete any unusable credential before creating a replacement.

## Use IAM keys with MCP

To give an MCP agent access to this mailbox, load the workload credential's secret as `NYLAS_API_KEY` and follow the [MCP client setup instructions](/docs/dev-guide/mcp/#manual-setup). Keep the management credential in your provisioning service.

Use the MCP endpoint for your application's region and the bound grant ID. Verify that the agent can read messages. After rotation, load the replacement secret and restart the client before deleting the old credential.

## What's next

- [IAM principals API reference](/docs/reference/api/iam-principals/)
- [IAM credentials API reference](/docs/reference/api/iam-credentials/)
- [Application API-key management](/docs/cookbook/use-cases/build/manage-api-keys/) for the separate application-wide key API
- [Security best practices](/docs/dev-guide/best-practices/) for credential storage and least privilege