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 appsGet OAuth App (
GET /api/v1/oauth-apps/{id}): retrieve a single OAuth app by its encoded IDList OAuth App Accounts (
GET /api/v1/oauth-apps/{id}/accounts): page the accounts that have granted one appRevoke OAuth App Account Grant (
POST /api/v1/oauth-apps/{id}/accounts/{accountId}/revoke): revoke one account's grant of an appUpdate 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
nextCursorvalue from one response as thecursorparameter in the next request.The
limitparameter controls page size and is locked into the cursor, so later pages keep the page size of the first request.When
hasMoreis false andnextCursoris 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
clientIdwhen you need exactness, and re-fetch specific apps with theclientIdfilter.
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.
Material Security API key
The pagination cursor returned in a previous response. Pass this value to retrieve the next page of results.
eyJ2IjoxLCJvIjoyNSwibCI6MjV9The maximum number of items to return per page.
25Filter 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.
tnt.1.ZXhhbXBsZV9pZF8xMjMFilter 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.
1234567890-abcdefghijklmnop.apps.googleusercontent.comFilter 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.
suspicious,maliciousFilter 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.
revokePossible values: 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.
Filter by whether Google's brand verification returned a verified brand record.
Filter to apps whose brand belongs to one of your own domains. false also matches apps where this has not been determined.
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.
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.
gmail,driveCase-insensitive substring match on the app name or the Google OAuth client ID.
acmeSort 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.
lastNewGrant:descSuccessful response
Bad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
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"
]
}
}
]
}
}
]
}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.
Material Security API key
The encoded OAuth app ID. Format: oapp.1.<base64>.
oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$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.
accounts,investigationSuccessful response
A third-party OAuth application observed holding token grants in your Google Workspace tenant. Google Workspace is the only provider covered today.
The encoded OAuth app ID. Format: oapp.1.<base64>.
oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$The encoded ID of the tenant the app was observed in. Format: tnt.1.<base64>.
tnt.1.ZXhhbXBsZV9pZF8xMjMThe 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.
Acme CRMThe Google OAuth client ID. This is the stable identity of the app and the value that appears in Google Workspace audit logs.
1234567890-abcdefghijklmnop.apps.googleusercontent.comTrue when the app's registered brand belongs to one of your own domains (for example an Apps Script written in-house). False when unknown.
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.
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.
["https://www.googleapis.com/auth/drive","openid"]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.
safePossible values: Bad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
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"
]
}
}
]
}
}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.
Material Security API key
The encoded OAuth app ID. Format: oapp.1.<base64>.
oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$Only provided fields are changed. An empty body is a no-op.
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.
suspiciousPossible values: Successful response
A third-party OAuth application observed holding token grants in your Google Workspace tenant. Google Workspace is the only provider covered today.
The encoded OAuth app ID. Format: oapp.1.<base64>.
oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$The encoded ID of the tenant the app was observed in. Format: tnt.1.<base64>.
tnt.1.ZXhhbXBsZV9pZF8xMjMThe 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.
Acme CRMThe Google OAuth client ID. This is the stable identity of the app and the value that appears in Google Workspace audit logs.
1234567890-abcdefghijklmnop.apps.googleusercontent.comTrue when the app's registered brand belongs to one of your own domains (for example an Apps Script written in-house). False when unknown.
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.
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.
["https://www.googleapis.com/auth/drive","openid"]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.
safePossible values: Bad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
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"
]
}
}
]
}
}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.
Material Security API key
The encoded OAuth app ID. Format: oapp.1.<base64>.
oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$The pagination cursor returned in a previous response. Pass this value to retrieve the next page of results.
eyJ2IjoxLCJvIjoyNSwibCI6MjV9The maximum number of items to return per page.
25Filter to accounts that currently grant the app (true) or that no longer do (false). Omit to list every account that ever granted it.
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.
Successful response
Bad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
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"
]
}
}
]
}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.
Material Security API key
The encoded OAuth app ID. Format: oapp.1.<base64>.
oapp.1.MDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMDAwPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$The encoded ID of the account whose grant of this app is revoked. Format: acct.1.<base64>.
acct.1.ZXhhbXBsZV9pZF8xMjMPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$Successful response
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.
The encoded account ID. Format: acct.1.<base64>.
acct.1.ZXhhbXBsZV9pZF8xMjMNull 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.
user@example.comNull when Material holds no display name for the account, or when your token cannot read the account, on the same terms as email.
Jane DoeBad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
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?

