For the complete documentation index, see llms.txt. This page is also available as Markdown.

OAuth Apps

Overview

The OAuth Apps API provides programmatic access to the third-party applications that hold OAuth token grants in your tenant. Google Workspace is the only provider covered today, so every app returned is a Google OAuth client. Each app exposes its Google client ID, the scopes currently granted across your accounts, the restricted-scope families those scopes fall into, brand metadata and verification status, its committed classification, any classification recommended by Material's OAuth Remediation Agent, and the standing remediation policy assigned to it.

Five endpoints make up the OAuth Apps API:

  • List OAuth Apps (GET /api/v1/oauth-apps): filter, sort, and page through OAuth apps

  • Get OAuth App (GET /api/v1/oauth-apps/{id}): retrieve a single OAuth app by its encoded ID

  • List OAuth App Accounts (GET /api/v1/oauth-apps/{id}/accounts): page the accounts that have granted one app

  • Revoke OAuth App Account Grant (POST /api/v1/oauth-apps/{id}/accounts/{accountId}/revoke): revoke one account's grant of an app

  • Update OAuth App (PATCH /api/v1/oauth-apps/{id}): set the classification and the remediation policy

Filtering

List accepts search (substring match over app name or client ID), clientId (exact match, the way to resolve a client ID from a Google Workspace audit log), classification, remediation, hasActiveAccounts, verified, internal, loginOnly, restrictedScopeType, and tenant (encoded tenant IDs; always intersected with the tenants your token can reach, so an unreachable ID matches nothing). Comma-separated values match any of the listed values within a single filter, and every filter you add narrows the result set further. By default the list returns every OAuth app ever observed, including apps whose grants have all been revoked; pass hasActiveAccounts=true to narrow to apps still in use.

The console labels an app with no classification "Unknown", which is classification: null here and the none filter value. For what each classification means, see OAuth Detections and Classification.

The classification filter matches the effective classification, the same value Explorer > Apps > OAuth shows: while the OAuth Remediation Agent has a recommendation staged, that recommendation is matched in place of the committed value. A matching app can therefore come back with classification: null (or a different committed value) and the matched value in recommendedClassification. There is no committed-only filter mode: an app whose committed value differs from a staged recommendation will not match a classification query for that committed value at all. To work with committed values, list without the classification filter and compare the classification field yourself.

Sorting

Sort with ?sort=<field>:asc|desc. Supported fields: name, firstObserved, lastNewGrant, and activeAccounts. One field at a time; the default is firstObserved:desc. activeAccounts orders by a cached count that a tenant-wide revoke does not immediately refresh.

Accounts

An OAuth app is only as dangerous as the accounts that granted it, so each app has an accounts collection: List OAuth App Accounts pages it, and include=accounts on Get OAuth App inlines its first page. Each item is the account (encoded ID, email, display name) plus an edge object describing that one account's grant: whether it is still active, when it was first and most recently granted, and the scopes in that grant, which can be narrower than the app-level scopes union.

By default every account that has ever granted the app is listed, revoked grants included, ordered by when each account first granted it, newest first. Pass active=true for the accounts that currently grant it, or active=false for the ones that no longer do; providerStatus narrows by the account's own state instead, and providerStatus=active is the population the console's Accounts tab shows for the app. meta.numActive counts the currently granting accounts across the whole app and does not move when you filter, so it stays comparable against meta.totalCount. An edge.active of false covers every way a grant ends: the user revoked it, Material revoked it, or the account was suspended or deleted.

The include Parameter

Pass include=accounts on Get OAuth App to inline the first page (up to 100) of the accounts collection above. A meta.hasMore of true alongside a null meta.nextCursor means the app has more accounts than fit inline; page them at List OAuth App Accounts.

Pass include=investigation on Get OAuth App to also return the most recent run of Material's OAuth Remediation Agent: its status, timestamps, overall risk narrative, and per-dimension assessments (vendor trust, scope risk, blast radius, and app behavior). Reading it never starts a new run, and it is null whenever there is no investigation to return. This include requires permission to read investigation data in addition to app access; a token without it gets a 403 rather than a response with the field quietly missing, so a null always means "there is nothing to show" and never "not allowed to see it". The narrative fields are written by the OAuth Remediation Agent and quote app metadata the app publisher controls, so treat them as untrusted text.

Pass include=* to return both.

Revoking One Account's Grant

Revoke OAuth App Account Grant revokes a single account's grant of an app at the provider and returns that account's updated entry from the accounts collection.

This is a one-off action, not a policy. It leaves the app's remediation untouched, so if the account grants the app again, Material won't revoke it automatically. Use remediation when you want that. The whole grant goes rather than selected scopes, because the provider offers no per-scope handle. Revoking a grant that has already ended succeeds and changes nothing.

A 403 here means OAuth app remediation is turned off for that account, which is a per-account setting rather than a property of your token.

Updating an App

An app's identity, scopes, brand, and grant history are observed from Google and read-only. The two fields you can set are classification and remediation; only fields present in the body change.

remediation is a standing policy, not a one-shot action. ["revoke"] or ["revoke", "notify"] tells Material to keep revoking this app's grants, including for accounts that grant it later. The work is asynchronous, so a 200 records the policy rather than confirming a revocation. [] removes the policy, which stops future enforcement but does not restore revoked grants. notify cannot be sent alone, since it means notifying the accounts whose grants were revoked.

Setting classification resolves a staged agent recommendation that carries an issueId to accepted or rejected and mirrors onto the app's open investigation issues. Classification never implies a remediation: set both fields explicitly when you want both. Because remediation is a standing policy, any update to an app that already carries one, including a classification-only update, can enforce it against accounts that have granted the app since the last enforcement pass. There is no way to return an app to unclassified.

Response Structure

List responses follow the standard two-part structure: a meta object (with totalCount, limit, hasMore, and nextCursor) and an items array. List OAuth Apps items are OAuth app objects; List OAuth App Accounts items are accounts carrying the grant under edge, and its meta adds numActive.

Want a walkthrough?

Review Hello World.

Pagination and Rate Limiting

  • Material APIs use cursor-based pagination: pass the nextCursor value from one response as the cursor parameter in the next request.

  • The limit parameter controls page size and is locked into the cursor, so later pages keep the page size of the first request.

  • When hasMore is false and nextCursor is null, you've reached the last page.

  • Each page is a point-in-time window over a live inventory. Apps sort in a stable total order, but an app observed while you are paging shifts later rows, so consecutive pages can still repeat or skip one. Reconcile paged results by clientId when you need exactness, and re-fetch specific apps with the clientId filter.

List OAuth Apps

get

Retrieves a paginated list of the third-party OAuth applications observed holding token grants in your Google Workspace tenant (the only provider covered today). By default every app ever observed is returned, including ones whose grants have all been revoked; use hasActiveAccounts to narrow to apps in use. Filter by classification, remediation policy, brand verification, restricted scopes, and more, and sort by name, grant times, or account count. The classification filter matches the effective classification Explorer > Apps > OAuth shows, so an app with a staged agent recommendation matches that recommendation rather than its committed value.

Authorizations
x-material-client-secretstringRequired

Material Security API key

Query parameters
cursorstringOptional

The pagination cursor returned in a previous response. Pass this value to retrieve the next page of results.

Example: eyJ2IjoxLCJvIjoyNSwibCI6MjV9
limitintegerOptional

The maximum number of items to return per page.

Example: 25
tenantstringOptional

Filter to apps observed in any of these tenants, comma-separated encoded tenant IDs (format tnt.1.<base64>). Matches the tenantId field on each app; the param is named tenant to match the same filter on other resources. Results are always intersected with the tenants your API token can reach, so an id you cannot access matches nothing rather than erroring.

Example: tnt.1.ZXhhbXBsZV9pZF8xMjM
clientIdstringOptional

Filter to these Google OAuth client IDs, comma-separated. Exact, case-sensitive match on the clientId field, unlike search, which is a substring match over the name as well. A client ID is unique within a tenant, so this is the way to resolve a client ID seen in a Google Workspace audit log to an app, and the way to reconcile paged results exactly.

Example: 1234567890-abcdefghijklmnop.apps.googleusercontent.com
classificationstringOptional

Filter by classification, comma-separated. Values: safe (Safe), unnecessary (Unnecessary), overprivileged (Overprivileged), suspicious (Suspicious), malicious (Malicious), plus none for apps with no classification, which Explorer > Apps > OAuth labels Unknown. Matching uses the effective classification: while the OAuth Remediation Agent has a recommendation staged, that recommendation is matched in place of the committed value. A matching app can therefore come back with classification: null (or a different committed value) and the matched value in recommendedClassification.

Example: suspicious,malicious
remediationstring · enumOptional

Filter to apps with a standing remediation policy. revoke matches any policy; notify matches the subset that also notifies, so notify results are always a subset of revoke results.

Example: revokePossible values:
hasActiveAccountsbooleanOptional

Filter to apps that currently have at least one account granting them (true) or none (false). Omit to list every OAuth app ever observed, including fully revoked ones. Evaluated against a cached count that a tenant-wide revoke does not immediately refresh, so an app can still match true for a while after its last grant ends; meta.numActive on the accounts collection is the live figure.

verifiedbooleanOptional

Filter by whether Google's brand verification returned a verified brand record.

internalbooleanOptional

Filter to apps whose brand belongs to one of your own domains. false also matches apps where this has not been determined.

loginOnlybooleanOptional

Filter to apps whose currently granted scopes are sign-in scopes only. false matches only apps Material has computed this for, so an app it never computed reads isLoginOnly: false yet matches neither filter value. That population is apps with no grant records left, which are also the apps hasActiveAccounts=false returns.

restrictedScopeTypestringOptional

Filter to apps granted any of these restricted-scope families, comma-separated. Values: gmail, drive, google_fit, google_chat, data_portability, photos_ambient. Matches on restrictedScopeTypes, so it carries that field's staleness.

Example: gmail,drive
searchstringOptional

Case-insensitive substring match on the app name or the Google OAuth client ID.

Example: acme
sortstringOptional

Sort field, e.g. name:asc or lastNewGrant:desc. One field only. Supported: name, firstObserved, lastNewGrant, activeAccounts. Defaults to firstObserved:desc. activeAccounts orders by a cached count that a tenant-wide revoke does not immediately refresh. Apps with equal sort values fall back to a stable internal order, so the sort is deterministic; an app observed while you are paging can still shift later rows, so reconcile by clientId when exactness matters.

Example: lastNewGrant:desc
Responses
200

Successful response

application/json
get/api/v1/oauth-apps
GET /api/v1/oauth-apps HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Accept: */*
{
  "meta": {
    "totalCount": 1,
    "limit": 1,
    "hasMore": true,
    "nextCursor": "text"
  },
  "items": [
    {
      "id": "oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAw",
      "tenantId": "tnt.1.ZXhhbXBsZV9pZF8xMjM",
      "name": "Acme CRM",
      "clientId": "1234567890-abcdefghijklmnop.apps.googleusercontent.com",
      "isInternal": true,
      "isLoginOnly": true,
      "scopes": [
        "https://www.googleapis.com/auth/drive",
        "openid"
      ],
      "restrictedScopeTypes": [
        "gmail",
        "drive"
      ],
      "brand": {
        "verified": true,
        "displayName": "Acme CRM",
        "supportEmail": "text",
        "homePageUrl": "text",
        "iconUrl": "text"
      },
      "classification": "safe",
      "recommendedClassification": {
        "value": "overprivileged",
        "status": "staged",
        "issueId": "iss.1.ZXhhbXBsZV9pZF8xMjM"
      },
      "remediation": [
        "revoke"
      ],
      "keyEvents": {
        "created": {
          "at": "2026-01-01T00:00:00.000Z"
        },
        "firstObserved": {
          "at": "2026-01-01T00:00:00.000Z"
        },
        "lastNewGrant": {
          "at": "2026-01-01T00:00:00.000Z"
        },
        "classificationChanged": {
          "at": "2026-01-01T00:00:00.000Z"
        },
        "remediationChanged": {
          "at": "2026-01-01T00:00:00.000Z"
        }
      },
      "investigation": {
        "status": "running",
        "startedAt": "2026-01-01T00:00:00.000Z",
        "completedAt": "2026-01-01T00:00:00.000Z",
        "riskAssessment": "text",
        "assessments": {
          "vendorTrust": {
            "level": "very_high",
            "summary": "text"
          },
          "scopeRisk": {
            "level": "very_low",
            "summary": "text"
          },
          "blastRadius": {
            "level": "small",
            "summary": "text"
          },
          "appBehavior": {
            "level": "benign",
            "summary": "text"
          }
        }
      },
      "accounts": {
        "meta": {
          "totalCount": 1,
          "limit": 1,
          "hasMore": true,
          "nextCursor": "text",
          "numActive": 1
        },
        "items": [
          {
            "id": "acct.1.ZXhhbXBsZV9pZF8xMjM",
            "email": "user@example.com",
            "displayName": "Jane Doe",
            "edge": {
              "active": true,
              "firstGrantedAt": "2026-01-01T00:00:00.000Z",
              "lastGrantedAt": "2026-01-01T00:00:00.000Z",
              "scopes": [
                "https://www.googleapis.com/auth/drive.readonly"
              ]
            }
          }
        ]
      }
    }
  ]
}

Get OAuth App

get

Retrieves a single OAuth application by its encoded ID, including its Google client ID, currently granted scopes, brand metadata, classification, and standing remediation policy. Use include=accounts to also return the first page of the accounts that have granted it, and include=investigation for the most recent run of Material's OAuth Remediation Agent.

Authorizations
x-material-client-secretstringRequired

Material Security API key

Path parameters
idstringRequired

The encoded OAuth app ID. Format: oapp.1.<base64>.

Example: oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$
Query parameters
includestringOptional

Comma-separated list of extra fields to return. Supported: accounts, the first page of the accounts edge; and investigation, which additionally requires permission to read investigation data and returns 403 without it. Use * for all of them.

Example: accounts,investigation
Responses
200

Successful response

application/json

A third-party OAuth application observed holding token grants in your Google Workspace tenant. Google Workspace is the only provider covered today.

idstringRequired

The encoded OAuth app ID. Format: oapp.1.<base64>.

Example: oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$
tenantIdstringRequired

The encoded ID of the tenant the app was observed in. Format: tnt.1.<base64>.

Example: tnt.1.ZXhhbXBsZV9pZF8xMjM
namestringRequired

The name Google shows on the consent screen. The app publisher chooses it, so it can impersonate a well-known vendor; use clientId to identify an app.

Example: Acme CRM
clientIdstringRequired

The Google OAuth client ID. This is the stable identity of the app and the value that appears in Google Workspace audit logs.

Example: 1234567890-abcdefghijklmnop.apps.googleusercontent.com
isInternalbooleanRequired

True when the app's registered brand belongs to one of your own domains (for example an Apps Script written in-house). False when unknown.

isLoginOnlybooleanRequired

True when every scope currently granted is a sign-in scope (email, profile, or openid), so the app can identify users but read nothing. Derived from the same observed grants as scopes, so it carries the same staleness, and it reads false for an app Material has never computed it for.

scopesstring[]Required

The union of scopes across the grants Material last observed as active, sorted. This can lag in both directions: a very recent grant may not appear yet, and after every account is revoked at once it can keep listing scopes no account still holds. Use the accounts collection for live per-account grant state.

Example: ["https://www.googleapis.com/auth/drive","openid"]
classificationstring · enum · nullableRequired

The committed classification of the app, set by an admin or committed automatically by a safe investigation verdict. Null means unclassified, which Explorer > Apps > OAuth labels Unknown. The list classification filter matches the effective classification instead, so a filtered result can carry a value here that differs from the one you filtered on. The effective value is derivable from this payload: it is recommendedClassification.value while that object has status: 'staged', and this field otherwise.

Example: safePossible values:
get/api/v1/oauth-apps/{id}
GET /api/v1/oauth-apps/{id} HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Accept: */*
{
  "id": "oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAw",
  "tenantId": "tnt.1.ZXhhbXBsZV9pZF8xMjM",
  "name": "Acme CRM",
  "clientId": "1234567890-abcdefghijklmnop.apps.googleusercontent.com",
  "isInternal": true,
  "isLoginOnly": true,
  "scopes": [
    "https://www.googleapis.com/auth/drive",
    "openid"
  ],
  "restrictedScopeTypes": [
    "gmail",
    "drive"
  ],
  "brand": {
    "verified": true,
    "displayName": "Acme CRM",
    "supportEmail": "text",
    "homePageUrl": "text",
    "iconUrl": "text"
  },
  "classification": "safe",
  "recommendedClassification": {
    "value": "overprivileged",
    "status": "staged",
    "issueId": "iss.1.ZXhhbXBsZV9pZF8xMjM"
  },
  "remediation": [
    "revoke"
  ],
  "keyEvents": {
    "created": {
      "at": "2026-01-01T00:00:00.000Z"
    },
    "firstObserved": {
      "at": "2026-01-01T00:00:00.000Z"
    },
    "lastNewGrant": {
      "at": "2026-01-01T00:00:00.000Z"
    },
    "classificationChanged": {
      "at": "2026-01-01T00:00:00.000Z"
    },
    "remediationChanged": {
      "at": "2026-01-01T00:00:00.000Z"
    }
  },
  "investigation": {
    "status": "running",
    "startedAt": "2026-01-01T00:00:00.000Z",
    "completedAt": "2026-01-01T00:00:00.000Z",
    "riskAssessment": "text",
    "assessments": {
      "vendorTrust": {
        "level": "very_high",
        "summary": "text"
      },
      "scopeRisk": {
        "level": "very_low",
        "summary": "text"
      },
      "blastRadius": {
        "level": "small",
        "summary": "text"
      },
      "appBehavior": {
        "level": "benign",
        "summary": "text"
      }
    }
  },
  "accounts": {
    "meta": {
      "totalCount": 1,
      "limit": 1,
      "hasMore": true,
      "nextCursor": "text",
      "numActive": 1
    },
    "items": [
      {
        "id": "acct.1.ZXhhbXBsZV9pZF8xMjM",
        "email": "user@example.com",
        "displayName": "Jane Doe",
        "edge": {
          "active": true,
          "firstGrantedAt": "2026-01-01T00:00:00.000Z",
          "lastGrantedAt": "2026-01-01T00:00:00.000Z",
          "scopes": [
            "https://www.googleapis.com/auth/drive.readonly"
          ]
        }
      }
    ]
  }
}

Update OAuth App

patch

Updates an OAuth app's classification and standing remediation policy. All other fields are read-only. Only provided fields change. A remediation containing revoke queues revocation of the app's token grants asynchronously, so a 200 records the policy rather than confirming any token is revoked; [] removes the policy. Setting a classification also resolves a staged agent recommendation that carries an issueId, and mirrors onto the app's open investigation issues.

Authorizations
x-material-client-secretstringRequired

Material Security API key

Path parameters
idstringRequired

The encoded OAuth app ID. Format: oapp.1.<base64>.

Example: oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$
Body

Only provided fields are changed. An empty body is a no-op.

classificationstring · enumOptional

Commit a classification. A staged agent recommendation that carries an issueId resolves to accepted or rejected; the value also mirrors onto this app's open investigation issues. There is no way back to unclassified through this API.

Example: suspiciousPossible values:
Responses
200

Successful response

application/json

A third-party OAuth application observed holding token grants in your Google Workspace tenant. Google Workspace is the only provider covered today.

idstringRequired

The encoded OAuth app ID. Format: oapp.1.<base64>.

Example: oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$
tenantIdstringRequired

The encoded ID of the tenant the app was observed in. Format: tnt.1.<base64>.

Example: tnt.1.ZXhhbXBsZV9pZF8xMjM
namestringRequired

The name Google shows on the consent screen. The app publisher chooses it, so it can impersonate a well-known vendor; use clientId to identify an app.

Example: Acme CRM
clientIdstringRequired

The Google OAuth client ID. This is the stable identity of the app and the value that appears in Google Workspace audit logs.

Example: 1234567890-abcdefghijklmnop.apps.googleusercontent.com
isInternalbooleanRequired

True when the app's registered brand belongs to one of your own domains (for example an Apps Script written in-house). False when unknown.

isLoginOnlybooleanRequired

True when every scope currently granted is a sign-in scope (email, profile, or openid), so the app can identify users but read nothing. Derived from the same observed grants as scopes, so it carries the same staleness, and it reads false for an app Material has never computed it for.

scopesstring[]Required

The union of scopes across the grants Material last observed as active, sorted. This can lag in both directions: a very recent grant may not appear yet, and after every account is revoked at once it can keep listing scopes no account still holds. Use the accounts collection for live per-account grant state.

Example: ["https://www.googleapis.com/auth/drive","openid"]
classificationstring · enum · nullableRequired

The committed classification of the app, set by an admin or committed automatically by a safe investigation verdict. Null means unclassified, which Explorer > Apps > OAuth labels Unknown. The list classification filter matches the effective classification instead, so a filtered result can carry a value here that differs from the one you filtered on. The effective value is derivable from this payload: it is recommendedClassification.value while that object has status: 'staged', and this field otherwise.

Example: safePossible values:
patch/api/v1/oauth-apps/{id}
PATCH /api/v1/oauth-apps/{id} HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 56

{
  "classification": "suspicious",
  "remediation": [
    "revoke"
  ]
}
{
  "id": "oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAw",
  "tenantId": "tnt.1.ZXhhbXBsZV9pZF8xMjM",
  "name": "Acme CRM",
  "clientId": "1234567890-abcdefghijklmnop.apps.googleusercontent.com",
  "isInternal": true,
  "isLoginOnly": true,
  "scopes": [
    "https://www.googleapis.com/auth/drive",
    "openid"
  ],
  "restrictedScopeTypes": [
    "gmail",
    "drive"
  ],
  "brand": {
    "verified": true,
    "displayName": "Acme CRM",
    "supportEmail": "text",
    "homePageUrl": "text",
    "iconUrl": "text"
  },
  "classification": "safe",
  "recommendedClassification": {
    "value": "overprivileged",
    "status": "staged",
    "issueId": "iss.1.ZXhhbXBsZV9pZF8xMjM"
  },
  "remediation": [
    "revoke"
  ],
  "keyEvents": {
    "created": {
      "at": "2026-01-01T00:00:00.000Z"
    },
    "firstObserved": {
      "at": "2026-01-01T00:00:00.000Z"
    },
    "lastNewGrant": {
      "at": "2026-01-01T00:00:00.000Z"
    },
    "classificationChanged": {
      "at": "2026-01-01T00:00:00.000Z"
    },
    "remediationChanged": {
      "at": "2026-01-01T00:00:00.000Z"
    }
  },
  "investigation": {
    "status": "running",
    "startedAt": "2026-01-01T00:00:00.000Z",
    "completedAt": "2026-01-01T00:00:00.000Z",
    "riskAssessment": "text",
    "assessments": {
      "vendorTrust": {
        "level": "very_high",
        "summary": "text"
      },
      "scopeRisk": {
        "level": "very_low",
        "summary": "text"
      },
      "blastRadius": {
        "level": "small",
        "summary": "text"
      },
      "appBehavior": {
        "level": "benign",
        "summary": "text"
      }
    }
  },
  "accounts": {
    "meta": {
      "totalCount": 1,
      "limit": 1,
      "hasMore": true,
      "nextCursor": "text",
      "numActive": 1
    },
    "items": [
      {
        "id": "acct.1.ZXhhbXBsZV9pZF8xMjM",
        "email": "user@example.com",
        "displayName": "Jane Doe",
        "edge": {
          "active": true,
          "firstGrantedAt": "2026-01-01T00:00:00.000Z",
          "lastGrantedAt": "2026-01-01T00:00:00.000Z",
          "scopes": [
            "https://www.googleapis.com/auth/drive.readonly"
          ]
        }
      }
    ]
  }
}

List OAuth App Accounts

get

Retrieves the paginated list of accounts that have granted this OAuth app, ordered by when each account first granted it, newest first. By default every account that ever granted it is listed, including ones whose grants have ended; active narrows by grant state and providerStatus by account state, and the two are independent. Membership is decided by the grant rather than by the account directory, so this is not a filtered view of /accounts. Each item carries the scopes of that account's own grant, which can be narrower than the app-level union, and meta.numActive reports how many accounts currently grant the app regardless of the filters.

Authorizations
x-material-client-secretstringRequired

Material Security API key

Path parameters
idstringRequired

The encoded OAuth app ID. Format: oapp.1.<base64>.

Example: oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$
Query parameters
cursorstringOptional

The pagination cursor returned in a previous response. Pass this value to retrieve the next page of results.

Example: eyJ2IjoxLCJvIjoyNSwibCI6MjV9
limitintegerOptional

The maximum number of items to return per page.

Example: 25
activebooleanOptional

Filter to accounts that currently grant the app (true) or that no longer do (false). Omit to list every account that ever granted it.

providerStatusstring · enumOptional

Filter by the granting account's current state at your provider: active, suspended, archived, or deleted. Independent of the grant-level active filter: an active account can hold an ended grant, and a suspended one can still hold a live grant. Combine the two to narrow on both.

Possible values:
Responses
200

Successful response

application/json
get/api/v1/oauth-apps/{id}/accounts
GET /api/v1/oauth-apps/{id}/accounts HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Accept: */*
{
  "meta": {
    "totalCount": 1,
    "limit": 1,
    "hasMore": true,
    "nextCursor": "text",
    "numActive": 1
  },
  "items": [
    {
      "id": "acct.1.ZXhhbXBsZV9pZF8xMjM",
      "email": "user@example.com",
      "displayName": "Jane Doe",
      "edge": {
        "active": true,
        "firstGrantedAt": "2026-01-01T00:00:00.000Z",
        "lastGrantedAt": "2026-01-01T00:00:00.000Z",
        "scopes": [
          "https://www.googleapis.com/auth/drive.readonly"
        ]
      }
    }
  ]
}

Revoke OAuth App Account Grant

post

Revokes one account's grant of this OAuth app at the provider, and returns the updated entry from the app's accounts collection. This is a one-off action, not a policy: it leaves the app's remediation untouched, and if the account grants the app again it will not be revoked automatically. Use remediation for that. The whole grant goes, not selected scopes, because the provider offers no per-scope handle. Revoking a grant that has already ended succeeds and changes nothing. Returns 403 if OAuth app remediation is disabled for the account, which is a per-account setting rather than a property of your token.

Authorizations
x-material-client-secretstringRequired

Material Security API key

Path parameters
idstringRequired

The encoded OAuth app ID. Format: oapp.1.<base64>.

Example: oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$
accountIdstringRequired

The encoded ID of the account whose grant of this app is revoked. Format: acct.1.<base64>.

Example: acct.1.ZXhhbXBsZV9pZF8xMjMPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$
Body
objectOptional
Responses
200

Successful response

application/json

An account that has granted this OAuth app, with the grant itself under edge. An account appears here once it has ever granted the app, whether or not it still does. Membership is decided by the grant, not by the account directory, so this is not a filtered view of /accounts and does not apply that resource's rules about which accounts are listed. Every account here is a licensed member of the tenant whose grant Material recorded.

idstringRequired

The encoded account ID. Format: acct.1.<base64>.

Example: acct.1.ZXhhbXBsZV9pZF8xMjM
emailstring · nullableRequired

Null when Material holds no email address for the account, or when your token cannot read the account. The latter means the account has since moved to a domain your token cannot reach; the grant it made while in this one still lists, so the app's reach is not understated. Narrow with providerStatus to drop those rows.

Example: user@example.com
displayNamestring · nullableRequired

Null when Material holds no display name for the account, or when your token cannot read the account, on the same terms as email.

Example: Jane Doe
post/api/v1/oauth-apps/{id}/accounts/{accountId}/revoke
POST /api/v1/oauth-apps/{id}/accounts/{accountId}/revoke HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 2

{}
{
  "id": "acct.1.ZXhhbXBsZV9pZF8xMjM",
  "email": "user@example.com",
  "displayName": "Jane Doe",
  "edge": {
    "active": true,
    "firstGrantedAt": "2026-01-01T00:00:00.000Z",
    "lastGrantedAt": "2026-01-01T00:00:00.000Z",
    "scopes": [
      "https://www.googleapis.com/auth/drive.readonly"
    ]
  }
}

Last updated

Was this helpful?