> For the complete documentation index, see [llms.txt](https://docs.material.security/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.material.security/reference/api-v1/hello-world.md).

# Hello World

## Issues

This example walks you through making your first Issues API calls: listing open issues then retrieving the details of a specific one.

The workflow illustrates how the three Issues API endpoints interact to investigate a security issue. Depending on your needs and the level of detail required, you might use one or more endpoints.

<figure><img src="https://3441929823-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDXSwW7QZtKgywlqSARjL%2Fuploads%2FmNYilhjBhcQtEOmsIiHy%2Fimage.png?alt=media&#x26;token=5dc6cd60-abca-47bb-a7c1-5285c353460b" alt=""><figcaption></figcaption></figure>

<details>

<summary><strong>Step 1: Authenticate</strong></summary>

All Issues API requests must be authenticated using an API key (token), passed in the `x-material-client-secret` request header.

Follow the steps in Authentication to create a token in Material.

**Store your API token securely**

Avoid hardcoding your API token in scripts or source code. Instead, store it as an environment variable and reference it in your requests:

```bash
export MATERIAL_API_KEY="your_api_key_here"

curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/issues" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

#### Authentication errors

If your API token is missing or invalid, the API will return a `401 Unauthorized` response. Verify that the `x-material-client-secret` header is present and that the token is correct.

</details>

<details>

<summary><strong>Step 2: Find open issues</strong></summary>

Call the List Issues endpoint to retrieve a page of open issues from your instance.

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/issues?status=open" \
  --header "x-material-client-secret: YOUR_API_KEY"
```

A successful response returns a paginated collection of issues. Each item includes the issue's name, severity, classification, and the detection that triggered it.

Note the `id` field on any issue you want to investigate. You'll use it in the next step.

```json
{
  "meta": {
    "totalCount": 3,
    "limit": 25,
    "hasMore": false,
    "nextCursor": null
  },
  "items": [
    {
      "id": "ABC",
      "name": "Credential phishing attempt",
      "status": "open",
      "severity": "04-high",
      "classification": "malicious",
      ...
    }
  ]
}
```

</details>

<details>

<summary><strong>Step 3: Retrieve a specific issue</strong></summary>

Using the `id` from the previous response, call the Get Issue endpoint to retrieve the full details of that issue.

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/issues/{issue_id}" \
  --header "x-material-client-secret: YOUR_API_KEY"
```

The response returns the full issue object, including its severity, classification, primary entity, detection, and key lifecycle timestamps.

</details>

<details>

<summary><strong>Step 4: Retrieve the associated messages</strong></summary>

Examine the messages at the center of the issue by calling the List Issue Messages endpoint with the same issue ID.

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/issues/{issue_id}/messages" \
  --header "x-material-client-secret: YOUR_API_KEY"
```

The response returns a paginated list of messages, each with sender, recipients, subject, and attachment metadata. The `meta` object includes `numLinks` and `numAttachments` counts across all messages in the issue.

Use `cursor` and `limit` to page through large result sets.

</details>

<details>

<summary><strong>Next Steps</strong></summary>

Add `?include=comments,accounts` to any request to pull in analyst comments and associated accounts.

</details>

{% hint style="success" %}
Review the Issues API in more detail in the [API reference](/reference/api-v1/issues.md).
{% endhint %}

***

## Messages

This example walks you through making your first Messages API calls: searching for messages matching a query, polling for results, and retrieving a specific message.

<figure><img src="https://3441929823-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDXSwW7QZtKgywlqSARjL%2Fuploads%2FLWNQ3Tfeo1TxxO9MTGkI%2Fimage.png?alt=media&#x26;token=d07d2530-347b-4248-963e-4363d71f1361" alt="" width="563"><figcaption></figcaption></figure>

<details>

<summary><strong>Step 1: Authenticate</strong></summary>

All Messages API requests require an API key (token) passed in the `x-material-client-secret` request header. Store your token as an environment variable to avoid hardcoding it in your scripts:

```bash
export MATERIAL_API_KEY="your_api_key_here"
```

Then reference it in every request:

```bash
--header "x-material-client-secret: $MATERIAL_API_KEY"
```

If your API token is missing or invalid, the API returns a `401 Unauthorized` response.

</details>

<details>

<summary><strong>Step 2: Start a message search</strong></summary>

Call the Search Messages endpoint with a MQL query and a time window to find matching messages.

```bash
curl --request POST \
  --url "https://{your-instance.on.material.security}/api/v1/messages/search" \
  --header "x-material-client-secret: $MATERIAL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "mqlSearchString": "from:attacker@example.com subject:\"urgent payment\"",
    "startDate": "2024-01-01T00:00:00.000Z",
    "endDate": "2024-02-01T00:00:00.000Z"
  }'
```

The response returns a search ID and an initial status of `"CREATED"` or `"STARTED"`. Note the `id`. You'll use it to poll for results.

```json
{
  "id": "job.1.<base64>",
  "status": "CREATED"
}
```

</details>

<details>

<summary><strong>Step 3: Poll for results</strong></summary>

Call the Get Message Search Results endpoint using the search ID. Repeat until `status` is `"DONE"`.

```bash
curl --request GET \
  --url "https://{your-instance.on.material.security}/api/v1/messages/search/{search_id}" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

When the search is complete, the response includes a `data` object containing `meta` and `items` with the matching messages. If `meta.hasMore` is true, pass the `nextCursor` value to the `cursor` query parameter to retrieve the next page.

```json
{
  "id": "job.1.<base64>",
  "status": "DONE",
  "data": {
    "meta": {
      "totalCount": 2,
      "limit": 25,
      "hasMore": false,
      "nextCursor": null
    },
    "items": [
      {
        "id": "msg.1.<base64>",
        "date": "2024-01-15T12:00:00.000Z",
        "subject": "urgent payment",
        "from": "attacker@example.com",
        ...
      }
    ]
  }
}
```

</details>

<details>

<summary><strong>Step 4: Retrieve a specific message</strong></summary>

Using a message ID from the search results, retrieve the full details of that message.

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/messages/{message_id}" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

The response returns the full message object, including sender, recipients, subject, date, snippet, and attachment metadata.

</details>

<details>

<summary><strong>Next Steps</strong></summary>

Filter search results to specific tenants by passing `uDomainIds` in the search request body.

</details>

{% hint style="success" %}
Review the Messages API in more detail in the [API reference](/reference/api-v1/messages.md).
{% endhint %}

***

## Trusted Entities

This example walks you through making your first Trusted Entities API calls: reviewing what your instance already trusts, adding a new Trusted Entity, retrieving it to confirm its settings, and updating those settings.

Trusted Entities are the domains, email addresses, and IP addresses (or CIDR ranges) that Material treats as known and trusted. Adding or changing one affects what Material trusts, so every write is security-sensitive and audited.

<figure><img src="https://3441929823-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDXSwW7QZtKgywlqSARjL%2Fuploads%2FCyG96Hpj9eNYjzCxohOA%2Fimage.png?alt=media&amp;token=6fabe6aa-ea54-4b9d-b7a5-6ca48cf5767b" alt=""><figcaption></figcaption></figure>

<details>

<summary><strong>Step 1: Authenticate</strong></summary>

All Trusted Entities API requests require an API key (token) passed in the `x-material-client-secret` request header. Store your token as an environment variable to avoid hardcoding it in your scripts:

```bash
export MATERIAL_API_KEY="your_api_key_here"
```

Then reference it in every request:

```bash
--header "x-material-client-secret: $MATERIAL_API_KEY"
```

If your API token is missing or invalid, the API returns a `401 Unauthorized` response.

</details>

<details>

<summary><strong>Step 2: Review what's already trusted</strong></summary>

Call the List Trusted Entities endpoint to see the entities your instance already trusts. Material returns its built-in system entities first, followed by any you've added.

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/trusted-entities" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

Each item includes the entity value, its `type`, and its detection settings. To narrow the list, add query parameters such as `type=external_domain` or `enabled=true`.

```json
{
  "meta": {
    "limit": 25,
    "hasMore": false,
    "nextCursor": null
  },
  "items": [
    {
      "id": "te.1.<base64>",
      "entity": "example.com",
      "type": "external_domain",
      "tenant": null,
      "enabled": true,
      "subdomains": "included",
      "emailThreatDetection": "detect",
      "emailAuthentication": "enforced",
      "fileDetection": "treat_as_external"
    }
  ]
}
```

This list doesn't return a total count. Use `hasMore` and `nextCursor` to page through large result sets.

</details>

<details>

<summary><strong>Step 3: Add a Trusted Entity</strong></summary>

Call the Create Trusted Entity endpoint to add a new entity. Only `entity` and `type` are required. Any settings you leave out default to their least-permissive value.

```bash
curl --request POST \
  --url "https://your-instance.on.material.security/api/v1/trusted-entities" \
  --header "x-material-client-secret: $MATERIAL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "entity": "partner.example.com",
    "type": "external_domain",
    "subdomains": "included"
  }'
```

The response returns the created entity, including its generated `id`. Note the `id`. You'll use it to retrieve or update the entity.

```json
{
  "id": "te.1.<base64>",
  "entity": "partner.example.com",
  "type": "external_domain",
  "tenant": null,
  "enabled": true,
  "subdomains": "included",
  "emailThreatDetection": "detect",
  "emailAuthentication": "enforced",
  "fileDetection": "treat_as_external"
}
```

</details>

<details>

<summary><strong>Step 4: Retrieve the Trusted Entity</strong></summary>

Using the `id` from the previous response, call the Get Trusted Entity endpoint to confirm its settings.

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/trusted-entities/{entity_id}" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

The response returns the full Trusted Entity object. Add `?include=timeline` to see the change history for the entity, including when it was created and last modified.

</details>

<details>

<summary><strong>Step 5: Update its settings</strong></summary>

Call the Update Trusted Entity endpoint to change a setting. Only the fields you include change, and the `entity`, `type`, and `tenant` values can't be changed after creation.

For example, temporarily disable the entity without removing it:

```bash
curl --request PATCH \
  --url "https://your-instance.on.material.security/api/v1/trusted-entities/{entity_id}" \
  --header "x-material-client-secret: $MATERIAL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "enabled": false
  }'
```

The response returns the updated entity with your changes applied.

</details>

<details>

<summary><strong>Next Steps</strong></summary>

Apply up to 200 create, update, and delete operations in a single request with the Bulk Trusted Entities endpoint. To remove an entity you've added, call the Delete Trusted Entity endpoint with its `id`. System-managed entities can't be deleted.

</details>

{% hint style="success" %}
Review the Trusted Entities API in more detail in the [API reference](/reference/api-v1/trusted-entities.md).
{% endhint %}

***

## Accounts, Groups, and Roles

This example walks you through managing your directory: finding accounts, changing a setting for one of them, and granting a role. Accounts and groups are synced from Google, Microsoft, or Okta, so their identity and membership are read-only. What you can change is licenses, per-account and per-group setting overrides, and role assignments.

<details>

<summary><strong>Step 1: Authenticate</strong></summary>

All requests require an API token passed in the `x-material-client-secret` request header. Store your token as an environment variable to avoid hardcoding it in your scripts:

```bash
export MATERIAL_API_KEY="your_api_key_here"
```

Then reference it in every request:

```bash
--header "x-material-client-secret: $MATERIAL_API_KEY"
```

If your API token is missing or invalid, the API returns a `401 Unauthorized` response.

</details>

<details>

<summary><strong>Step 2: Find the accounts you care about</strong></summary>

Call the List Accounts endpoint. Filters combine, so this returns VIP accounts that currently have an active license:

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/accounts?vip=true&licensed=true" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

Each item includes the account's identity, VIP status, licenses, roles, and its effective settings with the layer each one resolves from.

```json
{
  "meta": { "totalCount": 12, "limit": 25, "hasMore": false, "nextCursor": null },
  "items": [
    {
      "id": "acct.1.<base64>",
      "type": "google",
      "displayName": "Jane Doe",
      "email": "jane@example.com",
      "vip": { "enabled": true, "emailAliases": [], "displayNames": [] },
      "licenses": { "values": ["advanced"], "source": "tenant" },
      "roles": [],
      "settings": {
        "emailThreatRemediation": { "enabled": true, "source": "default" }
      },
      "providerStatus": "active"
    }
  ]
}
```

You can also filter by `group`, `role`, `mailboxStatus`, `providerStatus`, or any single setting, for example `setting.emailThreatRemediation=off`.

</details>

<details>

<summary><strong>Step 3: Override a setting for one account</strong></summary>

Call the Update Account endpoint. Only the fields you send change.

```bash
curl --request PATCH \
  --url "https://your-instance.on.material.security/api/v1/accounts/{account_id}" \
  --header "x-material-client-secret: $MATERIAL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "settings": { "emailThreatRemediation": "off" }
  }'
```

The response returns the updated account. Its `settings.emailThreatRemediation.source` now reads `account`, showing the value comes from this account's own override rather than an inherited one.

To remove the override and go back to inheriting, send `"default"` instead of `"on"` or `"off"`.

</details>

<details>

<summary><strong>Step 4: See which roles you can assign</strong></summary>

Call the List Roles endpoint for the roles available on your instance. Role definitions are built in, so this catalog exists to tell you the valid IDs.

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/roles" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

Each role's `flavor` tells you how to assign it: `tenant` roles require a `tenants` list, `global` roles apply everywhere and reject one, and `individual` roles take neither.

```json
{
  "items": [
    { "id": "analyst", "name": "Analyst", "flavor": "tenant" },
    { "id": "super_admin", "name": "Super Admin", "flavor": "global" }
  ]
}
```

</details>

<details>

<summary><strong>Step 5: Grant a role</strong></summary>

Assign roles through the same Update Account endpoint. Only the roles you name are touched, so this is not a full replacement of the account's roles.

```bash
curl --request PATCH \
  --url "https://your-instance.on.material.security/api/v1/accounts/{account_id}" \
  --header "x-material-client-secret: $MATERIAL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "roles": {
      "assign": [{ "id": "analyst", "tenants": ["tnt.1.<base64>"] }]
    }
  }'
```

Changing roles requires role-management permission, and you can only grant roles you're allowed to bind yourself.

</details>

<details>

<summary><strong>Next Steps</strong></summary>

Groups work the same way. Call the List Groups endpoint to find a group, then Update Group to set its licenses, setting overrides, or the roles it grants. A role granted on a group applies to every member, and members see it on their own account with `source: "group"`.

Both resources accept up to 200 update operations in one call through their bulk endpoints.

</details>

{% hint style="success" %}
Review the [Accounts](/reference/api-v1/accounts.md), [Groups](/reference/api-v1/groups.md), and [Roles](/reference/api-v1/roles.md) APIs in more detail in the API reference.
{% endhint %}

***

## OAuth Apps

This example walks you through reviewing the third-party OAuth applications your accounts have granted, checking what Material's OAuth Remediation Agent concluded about one, and acting on it. Google Workspace is the only provider covered today.

<details>

<summary><strong>Step 1: Authenticate</strong></summary>

All requests require an API token passed in the `x-material-client-secret` request header. Store your token as an environment variable to avoid hardcoding it in your scripts:

```bash
export MATERIAL_API_KEY="your_api_key_here"
```

Then reference it in every request:

```bash
--header "x-material-client-secret: $MATERIAL_API_KEY"
```

If your API token is missing or invalid, the API returns a `401 Unauthorized` response.

</details>

<details>

<summary><strong>Step 2: Find apps still in use</strong></summary>

Call the List OAuth Apps endpoint. By default it returns every app ever observed, including ones whose grants have all been revoked, so narrow to apps still in use:

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/oauth-apps?hasActiveAccounts=true&sort=activeAccounts:desc" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

Each item includes the app's Google client ID, the scopes granted across your accounts, and its classification.

```json
{
  "items": [
    {
      "id": "oapp.1.<base64>",
      "name": "Acme CRM",
      "clientId": "1234567890-abcdefghijklmnop.apps.googleusercontent.com",
      "scopes": ["https://www.googleapis.com/auth/drive"],
      "restrictedScopeTypes": ["drive"],
      "classification": null,
      "remediation": []
    }
  ]
}
```

Use `clientId` to identify an app rather than `name`, which the app publisher chooses and can be made to imitate a known vendor.

</details>

<details>

<summary><strong>Step 3: Review one app in detail</strong></summary>

Call the Get OAuth App endpoint with both includes to pull the accounts that granted it and the agent's most recent assessment:

```bash
curl --request GET \
  --url "https://your-instance.on.material.security/api/v1/oauth-apps/{app_id}?include=accounts,investigation" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

`investigation` returns the agent's risk narrative and its per-dimension findings for vendor trust, scope risk, blast radius, and app behavior. Reading it never starts a new run. This include needs permission to read investigation data, and returns `403` without it.

`recommendedClassification` holds the agent's suggestion. While its `status` is `staged`, that suggestion is what Material shows in place of the committed `classification`.

{% hint style="warning" %}
The narrative fields quote app metadata the app publisher controls. Treat them as untrusted text and never render them as markup.
{% endhint %}

</details>

<details>

<summary><strong>Step 4: Classify the app and set a response</strong></summary>

Call the Update OAuth App endpoint. Setting a classification also resolves a staged recommendation, recording whether you accepted or rejected it.

```bash
curl --request PATCH \
  --url "https://your-instance.on.material.security/api/v1/oauth-apps/{app_id}" \
  --header "x-material-client-secret: $MATERIAL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "classification": "suspicious",
    "remediation": ["revoke", "notify"]
  }'
```

`remediation` is a standing policy, not a one-time action. It keeps revoking this app's grants, including for accounts that grant it later. The work is queued, so a `200` records the policy rather than confirming any token is revoked. Send `[]` to remove it, which stops future enforcement but doesn't restore grants already revoked.

Classification never implies a response, so set both fields when you want both.

</details>

<details>

<summary><strong>Step 5: Revoke one account's grant</strong></summary>

To revoke a single grant without setting a standing policy, call the Revoke OAuth App Account Grant endpoint:

```bash
curl --request POST \
  --url "https://your-instance.on.material.security/api/v1/oauth-apps/{app_id}/accounts/{account_id}/revoke" \
  --header "x-material-client-secret: $MATERIAL_API_KEY"
```

This is a one-off. It leaves the app's `remediation` untouched, so if that account grants the app again, Material won't revoke it automatically. A `403` here means OAuth App Remediation is turned off for that account.

</details>

<details>

<summary><strong>Next Steps</strong></summary>

Page the full list of accounts that granted an app with the List OAuth App Accounts endpoint. Each entry carries that account's own grant under `edge`, including the scopes it granted, which can be narrower than the app-level list.

</details>

{% hint style="success" %}
Review the [OAuth Apps](/reference/api-v1/oauth-apps.md) API in more detail in the API reference.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.material.security/reference/api-v1/hello-world.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
