# Glean Developer > Glean Developer - [Glean Developer](/index.md) ## oauth ### callback - [Signing in…](/oauth/callback.md) ## api Choose the right Glean API for your integration. - [Build with Glean APIs](/api.md): Choose the right Glean API for your integration. ### client-api Build user-facing applications with Glean's existing Client API capabilities. - [Client API](/api/client-api.md): Build user-facing applications with Glean's existing Client API capabilities. - [Report document activity](/api/client-api/activity/activity.md): Report user activity that occurs on indexed documents such as viewing or editing. This signal improves search quality. - [Report client activity](/api/client-api/activity/feedback.md): Report events that happen to results within a Glean client UI, such as search result views and clicks. This signal improves search quality. - [Activity API](/api/client-api/activity/overview.md): -be.glean.com/rest/api/v1/checkdatasourceauth?final_redirect_url=https%3A%2F%2Fyourapp.example.com%2Foauth-complete' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' ``` ### Tenant-Level Default (`oauth.postAuthRedirectUrl`)[​](#tenant-level-default-oauthpostauthredirecturl "Direct link to tenant-level-default-oauthpostauthredirecturl") A tenant-wide fallback configured by an administrator. When set, all datasource OAuth flows that don't specify a per-request `final_redirect_url` will redirect to this URL instead of the default Glean landing page. This is useful when all OAuth flows across your integration should redirect to the same destination. *** ## Redirect Resolution Order[​](#redirect-resolution-order "Direct link to Redirect Resolution Order") The redirect destination is resolved in this order: 1. **`final_redirect_url` query parameter** — if provided on the `checkdatasourceauth` request 2. **`oauth.postAuthRedirectUrl` tenant config** — if set by an administrator 3. **Default Glean landing page** — the standard fallback *** ## Example: Full Flow[​](#example-full-flow "Direct link to Example: Full Flow") 1 Call checkdatasourceauth with final\_redirect\_url Include the `final_redirect_url` query parameter to specify where users should land after completing OAuth. ``` curl -X POST 'https://-be.glean.com/rest/api/v1/checkdatasourceauth?final_redirect_url=https%3A%2F%2Fyourapp.example.com%2Foauth-complete' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' ``` 2 Receive the response The response includes datasource instances that need authorization. Each entry contains an `authUrlRelativePath` that already incorporates your redirect URL. ``` { "unauthorizedDatasourceInstances": [ { "datasourceInstance": "slack_0", "displayName": "Slack", "authStatus": "AWAITING_AUTH", "authUrlRelativePath": "/auth/datasource/slack_0?token=..." } ] } ``` 3 Construct the full OAuth URL Prepend your Glean backend base URL to the `authUrlRelativePath`: ``` https://-be.glean.com/ ``` 4 Redirect the user Send the user to the constructed URL. They complete the datasource's OAuth flow (e.g., authorizing Glean in Slack). 5 User lands at your redirect URL After completing OAuth, the user is redirected to the `final_redirect_url` you specified (`https://yourapp.example.com/oauth-complete` in this example) instead of the default Glean landing page. *** ## Related[​](#related "Direct link to Related") ### [checkdatasourceauth API Reference](/api/client-api/authentication/checkdatasourceauth.md) [Full API reference for the checkdatasourceauth endpoint](/api/client-api/authentication/checkdatasourceauth.md) ### [Web SDK Settings Component](/libraries/web-sdk/components/settings.md) [The Settings component handles datasource OAuth in embedded UI integrations](/libraries/web-sdk/components/settings.md) --- # Glean-Issued Tokens for Client API Glean-issued tokens provide an alternative to OAuth when you need granular scope control or don't have OAuth infrastructure. They're manually created through the admin console and offer fine-grained permissions. ### Glean Token Benefits * **Granular scope control** - Limit access to specific API endpoints * **User-scoped or global** - Choose appropriate permission level * **No external dependencies** - Works without identity provider setup * **Flexible expiration** - Set custom expiry dates warning Client API and Indexing API each support separately created Glean-issued credentials; a Client API token cannot be used as an Indexing API token. This guide focuses on Client API tokens. For Indexing API credentials, see [Indexing API Authentication](/api-info/indexing/authentication/overview.md). *** ## Authentication Headers[​](#authentication-headers "Direct link to Authentication Headers") Glean token requests use different headers based on token type: * User-Scoped Token * Global Token ``` Authorization: Bearer ``` ``` Authorization: Bearer X-Glean-ActAs: user@company.com ``` ### Header Details[​](#header-details "Direct link to Header Details") | Header | Required For | Description | Example Value | | --------------- | ------------------ | ---------------------------- | ------------------------ | | `Authorization` | All tokens | Bearer token authentication | `Bearer glean_XYZ123...` | | `X-Glean-ActAs` | Global tokens only | Email of user to impersonate | `user@company.com` | *** ## Token Types[​](#token-types "Direct link to Token Types") Choose the right token type for your use case: * User-Scoped (Recommended) * Global (Super Admin Only) **Best for**: Most applications, user-specific integrations * **Security**: Highest security, limited to specific user's data * **Headers**: Only `Authorization: Bearer ` required * **Creation**: Super Admins can create Glean-issued tokens for any user. Admins and API Token Creators can create Glean-issued tokens for themselves only. * **Use case**: Applications accessing data as a specific user ``` curl -X POST https://instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"query": "quarterly reports"}' ``` **Best for**: Multi-user applications, service integrations * **Security**: Can impersonate any user (use carefully) * **Headers**: Requires `X-Glean-ActAs` header with user email * **Creation**: Only Super Admins can create these tokens * **Use case**: Applications serving multiple users ``` curl -X POST https://instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'X-Glean-ActAs: user@company.com' \ -H 'Content-Type: application/json' \ -d '{"query": "quarterly reports"}' ``` warning The `X-Glean-ActAs` header is **required** for global tokens. Requests without this header will fail. *** ## Creating Tokens[​](#creating-tokens "Direct link to Creating Tokens") Follow these steps to create Glean-issued tokens: 1 Navigate to Token Management Go to [Client API Token Settings](https://app.glean.com/admin/platform/tokenManagement?tab=client) (admin access required) 2 Add New Token Click **Add New Token** and configure: * **Description**: Helpful name for tracking * **Permissions**: USER (recommended) or GLOBAL * **Scopes**: Select required API access scopes * **Expires**: Optional expiration date * **User Email**: Required for USER tokens 3 Save Token Securely warning The token secret is only shown once after creation. Save it securely - you cannot retrieve it again. *** ## Available Scopes[​](#available-scopes "Direct link to Available Scopes") Scopes control which Client API endpoints your token can access: Complete Scopes Reference | Scope | Description | Example Endpoints | | ---------------- | ------------------------ | ----------------------------- | | `ACTIVITY` | User activity collection | Activity reporting APIs | | `AGENTS` | Glean Agents | Agent creation, execution | | `ANNOUNCEMENTS` | Company announcements | Create, update announcements | | `ANSWERS` | Glean Answers | Manage Q\&A content | | `CHAT` | GleanChat functionality | Chat API, conversations | | `COLLECTIONS` | Content collections | Create, manage collections | | `DOCPERMISSIONS` | Document permissions | Access control management | | `DOCUMENTS` | Document operations | Document retrieval, metadata | | `ENTITIES` | Entity data | People, organizational data | | `FEEDBACK` | User feedback | Feedback collection | | `INSIGHTS` | Analytics insights | Usage analytics, reports | | `MCP` | Remote MCP server access | Connecting MCP hosts to Glean | | `PEOPLE` | People directory | Employee information | | `PINS` | Pinned content | Content pinning features | | `SEARCH` | Search functionality | Search queries, autocomplete | | `SHORTCUTS` | GoLinks/shortcuts | Shortcut management | | `SUMMARIZE` | AI summarization | Document summarization | | `TOOLS` | Glean Tools | List and execute tools | | `VERIFICATION` | Content verification | Document verification | warning Token permissions and scopes **cannot be changed** after creation. Plan carefully when creating tokens. *** ## Implementation Examples[​](#implementation-examples "Direct link to Implementation Examples") ### Search with User-Scoped Token[​](#search-with-user-scoped-token "Direct link to Search with User-Scoped Token") ``` curl -X POST https://instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "quarterly reports", "pageSize": 10 }' ``` ### Search with Global Token[​](#search-with-global-token "Direct link to Search with Global Token") ``` curl -X POST https://instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'X-Glean-ActAs: user@company.com' \ -H 'Content-Type: application/json' \ -d '{ "query": "quarterly reports", "pageSize": 10 }' ``` ### Chat with Glean Token[​](#chat-with-glean-token "Direct link to Chat with Glean Token") ``` curl -X POST https://instance-be.glean.com/rest/api/v1/chat \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "What are the latest quarterly results?", "conversationId": "optional-conversation-id" }' ``` *** ## Testing Glean Tokens[​](#testing-glean-tokens "Direct link to Testing Glean Tokens") ### Test Commands[​](#test-commands "Direct link to Test Commands") * User-Scoped Token * Global Token ``` curl -X POST https://-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"query": "test", "pageSize": 1}' ``` ``` curl -X POST https://-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'X-Glean-ActAs: your-email@company.com' \ -H 'Content-Type: application/json' \ -d '{"query": "test", "pageSize": 1}' ``` ### Expected Response[​](#expected-response "Direct link to Expected Response") Successful authentication returns a 200 status with search results: ``` { "results": [...], "trackingToken": "...", "requestId": "..." } ``` *** ## Troubleshooting Glean Tokens[​](#troubleshooting-glean-tokens "Direct link to Troubleshooting Glean Tokens") ### Common Token Errors[​](#common-token-errors "Direct link to Common Token Errors") | Error | Cause | Solution | | --------------------------------------------------------- | -------------------------------- | --------------------------------------- | | `401 Unauthorized` | Invalid or expired token | Verify token is correct and not expired | | `403 Forbidden` | Insufficient scopes | Add required scopes to token | | `400 Bad Request: Required header missing: X-Glean-ActAs` | Missing header for global token | Add `X-Glean-ActAs` header | | `401 Invalid identity` | Invalid email in `X-Glean-ActAs` | Verify email is valid and user exists | ### Debugging Steps[​](#debugging-steps "Direct link to Debugging Steps") 1 Verify token validity Check that your token hasn't expired and is correctly formatted 2 Confirm headers Ensure you have the correct headers for your token type: * User token: `Authorization` only * Global token: `Authorization` + `X-Glean-ActAs` 3 Check token scopes Verify your token has the required scopes for the endpoint you're accessing 4 Test with simple endpoint Start with a basic search request before testing complex operations *** ## Token Management Best Practices[​](#token-management-best-practices "Direct link to Token Management Best Practices") ### Security[​](#security "Direct link to Security") * **Use USER tokens** over GLOBAL tokens when possible * **Set expiration dates** for tokens used in temporary integrations * **Store tokens securely** - never commit tokens to version control * **Use environment variables** for token storage in applications * **Rotate tokens regularly** for long-lived integrations ### Development[​](#development "Direct link to Development") * **Create separate tokens** for development, staging, and production * **Use descriptive names** for tokens to track their purpose * **Test with minimal scopes** first, then expand as needed * **Plan token architecture** early - scopes cannot be changed after creation ### Monitoring[​](#monitoring "Direct link to Monitoring") * **Monitor token usage** through Glean's admin console * **Implement proper error handling** for authentication failures * **Log authentication events** for debugging and auditing * **Set up alerts** for token expiration *** ## Scope Planning Guide[​](#scope-planning-guide "Direct link to Scope Planning Guide") Choose the right scopes for your application: ### Common Scope Combinations[​](#common-scope-combinations "Direct link to Common Scope Combinations") | Use Case | Recommended Scopes | Notes | | ----------------------- | ---------------------------------- | ------------------------------------------- | | **Search Application** | `SEARCH`, `DOCUMENTS` | Basic search functionality | | **Chat Integration** | `CHAT`, `SEARCH` | Chat with search capabilities | | **Analytics Dashboard** | `INSIGHTS`, `ACTIVITY` | Usage analytics and reporting | | **Content Management** | `COLLECTIONS`, `PINS`, `SHORTCUTS` | Content organization features | | **Admin Tool** | Multiple scopes as needed | Consider global token for multi-user access | ### Scope Selection Tips[​](#scope-selection-tips "Direct link to Scope Selection Tips") * **Start minimal** - Add only scopes you immediately need * **Review regularly** - Audit which scopes are actually used * **Document decisions** - Record why specific scopes were chosen * **Test thoroughly** - Verify all required functionality works *** ## Next Steps[​](#next-steps "Direct link to Next Steps") ### [Client API Reference](/api/client-api.md) [Explore available Client API endpoints and their scope requirements](/api/client-api.md) ### [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) [Manage your Glean tokens in the admin console](https://app.glean.com/admin/platform/tokenManagement?tab=client) *** ## Migration from OAuth[​](#migration-from-oauth "Direct link to Migration from OAuth") If you're considering switching from OAuth to Glean tokens: ### When to Use Glean Tokens Instead of OAuth[​](#when-to-use-glean-tokens-instead-of-oauth "Direct link to When to Use Glean Tokens Instead of OAuth") * **Need Indexing API access** - Indexing API operations require separately created Glean-issued credentials and do not accept OAuth * **Don't have identity provider** - Glean tokens work independently * **Require global permissions** - Global tokens can impersonate users * **Need custom expiration** - More control over token lifecycle ### Migration Considerations[​](#migration-considerations "Direct link to Migration Considerations") * **Scope mapping** - Determine which scopes match your OAuth usage * **Header changes** - Update authentication headers in your code * **Permission model** - Understand user vs global token implications * **Testing** - Thoroughly test with new token type *** ## Need Help?[​](#need-help "Direct link to Need Help?") * **Token Creation**: Contact your Glean administrator for token creation access * **Scope Questions**: Review [Client API Reference](/api/client-api.md) for endpoint requirements * **Technical Issues**: Check troubleshooting section above * **Community**: Join discussions at [community.glean.com](https://community.glean.com) --- # OAuth Authentication for Client API OAuth is the **recommended authentication method for per-user Client API integrations**. You authenticate with an OAuth access token instead of managing a Glean-issued API token. There are two sources for that token, and they behave slightly differently on the wire: ### Glean OAuth Authorization Server **Glean issues the tokens (OAuth 2.1)** * Authorization Code flow with PKCE * Two registration modes: Dynamic Client Registration (DCR), subject to tenant policy, and admin-created static clients * Glean-defined, fine-grained scopes * Recognized by issuer — **no extra header** * Powers the [remote MCP server](/guides/mcp/.md) ### External Identity Provider **Your IdP issues the tokens** * Google, Okta, Azure Entra ID, OneLogin, etc. * Token lifecycle owned by your IdP * Requires the **`X-Glean-Auth-Type: OAUTH`** header * Reuses your existing enterprise auth warning This guide describes OAuth for the **Client API**. The Platform API supports the same customer-facing authentication methods through its [Platform authentication guide](/api/platform-api/authentication.md). Indexing API operations require [Glean-issued credentials](/api-info/indexing/authentication/overview.md) and do not accept OAuth. *** ## Authentication Headers[​](#authentication-headers "Direct link to Authentication Headers") Every OAuth request sends the access token as a bearer credential: ``` Authorization: Bearer ``` Whether you **also** need `X-Glean-Auth-Type: OAUTH` depends on who issued the token: | Token source | `X-Glean-Auth-Type: OAUTH` | | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | Glean OAuth Authorization Server (incl. Dynamic Client Registration) | Not required — Glean recognizes its own tokens by their issuer | | External identity provider (Google, Okta, Azure, etc.) | **Required** — without it the token is treated as a Glean API token and rejected with `401` | Using an SDK? The official API clients accept an OAuth access token in their existing token field. See the OAuth section of the [TypeScript](/libraries/api-clients/typescript.md#oauth-access-tokens), [Python](/libraries/api-clients/python.md#oauth-access-tokens), [Go](/libraries/api-clients/go.md#oauth-access-tokens), or [Java](/libraries/api-clients/java.md#oauth-access-tokens) client docs. *** ## Setup[​](#setup "Direct link to Setup") * Glean Authorization Server * External Identity Provider The [Glean OAuth Authorization Server](https://docs.glean.com/administration/oauth/authorization-server) is an OAuth 2.1 authorization server that issues access tokens for the Client API. It reuses your existing SSO. It does not replace your IdP. It is on by default. Tenants that already used IdP OAuth may have it off. 1 Confirm the authorization server if sign-in fails Start with OAuth. If sign-in fails, ask a Glean administrator to confirm that the Glean OAuth Authorization Server is enabled and that your client is allowed. 2 Choose a client-registration mode **Dynamic Client Registration (DCR)** lets the client register itself. A tenant can allow any application that supports DCR, restrict registration to approved applications using the Glean-managed list, custom redirect URI patterns, or both, or disable DCR entirely. DCR clients receive only the restricted scope set configured for DCR. See [Dynamic Client Registration](https://docs.glean.com/administration/oauth/dynamic-client-registration). A **static OAuth client** is an application an administrator registers and governs. Use a static client when DCR is disabled, when the tenant restricts DCR to approved applications and this application is not allowed, or when the application needs scopes DCR does not grant. See [static OAuth clients](https://docs.glean.com/administration/oauth/static-client-registration). 3 Obtain a token Use the Authorization Code flow with **PKCE**. Discover endpoints from the server metadata document and exchange the authorization code for an access token. For a static client, follow the [static-client token example](https://docs.glean.com/administration/oauth/static-client-token-example). For DCR, the host or application registers itself, then completes the same token flow. Endpoints (replace `` — see [finding your server URL](/get-started/authentication.md#finding-your-server-url)): | Purpose | URL | | --------------------------------------------------------- | ------------------------------------------------------------------------ | | OAuth server metadata | `https://-be.glean.com/.well-known/oauth-authorization-server` | | Token | `https://-be.glean.com/oauth/token` | | Dynamic Client Registration (when advertised and allowed) | `https://-be.glean.com/oauth/register` | The metadata document is the authoritative source for endpoints — fetch it to discover the current authorization, token, registration, and any other endpoints rather than relying on the values listed above. A registration endpoint does not mean every application, redirect URI, or scope is permitted to use DCR. Tokens from the Glean Authorization Server are recognized by their issuer, so requests **do not** need the `X-Glean-Auth-Type` header. Register an OAuth 2.1 application in your enterprise IdP and configure Glean to accept its tokens. See [OAuth with IdP-issued tokens](https://docs.glean.com/administration/oauth/oauth-idp) for the authoritative guide. 1 Configure your identity provider Set up an OAuth application in Google Workspace, Azure Entra ID, Okta, or OneLogin. Use the Authorization Code flow with **PKCE**; request the `offline_access` scope if you need a refresh token. 2 Enable OAuth in Glean In [Client API Settings](https://app.glean.com/admin/platform/tokenManagement?tab=client), enable **Allow OAuth token-based access**. 3 Register your Client ID and issuer Provide your OAuth application's Client ID and issuer so Glean can validate incoming tokens. 4 Send both headers Include `Authorization: Bearer ` **and** `X-Glean-Auth-Type: OAUTH` on every Client API request. Provider setup references: * Google Workspace (OIDC): [Google (OIDC)](https://docs.glean.com/administration/identity/sso/configuration/google-oidc) * Azure Entra ID (OIDC): [Azure (OIDC)](https://docs.glean.com/administration/identity/sso/configuration/entra-id-oidc) * Okta (SAML): [Okta (SAML)](https://docs.glean.com/administration/identity/sso/configuration/okta-saml) * OneLogin: [Generic SAML guide](https://docs.glean.com/administration/identity/sso/configuration/generic-saml) *** ## Implementation Examples[​](#implementation-examples "Direct link to Implementation Examples") The Glean Authorization Server example below shows a static-client token used with the Client API. To try the same authenticated endpoint interactively in your own tenant, use the [Search API Explorer](/api/client-api/search/search.md). API Explorer is for tenant testing only, not production sample code. * Glean Authorization Server token * External IdP token ``` curl -X POST https://-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "quarterly reports", "pageSize": 10 }' ``` ``` curl -X POST https://-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'X-Glean-Auth-Type: OAUTH' \ -H 'Content-Type: application/json' \ -d '{ "query": "quarterly reports", "pageSize": 10 }' ``` *** ## Token Properties[​](#token-properties "Direct link to Token Properties") * **Scope**: Governed by Glean [scopes](/api-info/client/authentication/glean-issued.md) (for example `SEARCH`, `CHAT`). For static clients, the allowed scopes are selected when the client is created or edited. Dynamically registered (DCR) clients receive only the restricted scope set configured for DCR, so use a static client when the scopes your application needs are not available through DCR. Request only the scopes your integration needs * **User context**: Treated as user-permissioned; permissions are enforced by Glean at request time * **Expiration & refresh**: Controlled by the issuer (Glean Authorization Server or your IdP). For a refresh token, request the `offline_access` scope and refresh with a standard OAuth library * **API support**: These setup instructions apply to Client API. For Platform API, see [Platform API Authentication](/api/platform-api/authentication.md). Indexing API does not support OAuth *** ## Troubleshooting OAuth[​](#troubleshooting-oauth "Direct link to Troubleshooting OAuth") | Error | Cause | Solution | | ------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | `401 Unauthorized` / `Invalid Secret` | External-IdP token sent without `X-Glean-Auth-Type: OAUTH`, so it was treated as a Glean API token | Add the `X-Glean-Auth-Type: OAUTH` header (external-IdP tokens only) | | `401 Unauthorized` | Invalid or expired token | Verify the token is valid and not expired; refresh if needed | | `403 Forbidden` | OAuth not enabled, or client ID / issuer mismatch | Confirm OAuth is enabled in Glean and the registered client ID / issuer matches the token | | `Invalid token format` | Malformed token | Verify the token is a valid JWT from your issuer | note If you are using the Glean OAuth Authorization Server and still see a missing-header error, confirm the token was issued by Glean's server (not your IdP). Glean-issued tokens are detected by issuer and need no header; external-IdP tokens always do. *** ## Best Practices[​](#best-practices "Direct link to Best Practices") ### Security[​](#security "Direct link to Security") * **Use HTTPS** for all OAuth flows and API requests * **Use Authorization Code + PKCE** — it is required by OAuth 2.1 and by the Glean Authorization Server * **Store tokens securely** — never commit them to version control * **Handle token refresh** gracefully using a standard OAuth library ### Production[​](#production "Direct link to Production") * **Use production OAuth applications** — don't ship development credentials * **Reuse an access token until it expires** rather than requesting a new one per call, and refresh once it expires * **Monitor authentication failures** through your issuer and Glean *** ## Next Steps[​](#next-steps "Direct link to Next Steps") ### [Use OAuth with an SDK](/libraries/api-clients/typescript.md#oauth-access-tokens) [Pass an OAuth token to the TypeScript, Python, Go, or Java client](/libraries/api-clients/typescript.md#oauth-access-tokens) ### [Client API Reference](/api/client-api.md) [Explore Client API endpoints that work with OAuth](/api/client-api.md) ### [Glean OAuth Authorization Server](https://docs.glean.com/administration/oauth/authorization-server) [Admin guide to the Glean OAuth Authorization Server and static OAuth clients](https://docs.glean.com/administration/oauth/authorization-server) ### [Remote MCP Server](/guides/mcp/.md) [OAuth-authenticated MCP access powered by the Glean Authorization Server](/guides/mcp/.md) *** ## Need Help?[​](#need-help "Direct link to Need Help?") * **Admin Setup**: Contact your Glean administrator for OAuth configuration * **Provider Issues**: Consult your identity provider documentation * **API Issues**: Check the [Client API Reference](/api/client-api.md) * **Community**: Join discussions at [community.glean.com](https://community.glean.com) --- # Client API Authentication This guide helps you choose and implement the right authentication method for Glean's **Client API**, which powers search, chat, collections, agents, and other user-facing functionality. ### OAuth (Recommended for per-user integrations) **Glean Authorization Server or your IdP** * Tokens from the Glean OAuth Authorization Server or an external IdP * Works with Google, Azure, Okta, OneLogin * Glean-defined scopes (Authorization Server) or Client API access via your IdP * DCR subject to tenant policy, or admin-created static clients when DCR is unavailable or does not grant the required scopes [Setup OAuth →](/api-info/client/authentication/oauth.md) ### Glean Tokens (Alternative) **Granular control and flexibility** * Fine-grained scope control * User-scoped or global permissions * Manual token management * Best for specific use cases or testing [Setup Glean Tokens →](/api-info/client/authentication/glean-issued.md) *** ## Quick Decision Guide[​](#quick-decision-guide "Direct link to Quick Decision Guide") Choose your authentication method based on your needs: ### Choose OAuth if you:[​](#choose-oauth-if-you "Direct link to Choose OAuth if you:") * Are building a per-user Client API integration * Want users to authorize access without distributing Glean-issued API tokens * Can use the Glean OAuth Authorization Server (on by default; tenants that already used IdP OAuth may have it off) or already use supported OAuth tokens from your enterprise identity provider * Want the token issuer to manage expiration and refresh ### Choose Glean-issued tokens if you:[​](#choose-glean-issued-tokens-if-you "Direct link to Choose Glean-issued tokens if you:") * Call the Indexing API, which does not accept OAuth * Need a global token with `X-Glean-ActAs` to impersonate users * Have no OAuth path: the Glean OAuth Authorization Server is disabled and no external-IdP OAuth path is configured *** ## Authentication Headers Reference[​](#authentication-headers-reference "Direct link to Authentication Headers Reference") Different authentication methods require different headers: * Glean OAuth Authorization Server * External IdP OAuth * User-Scoped Token * Global Token ``` Authorization: Bearer ``` ``` Authorization: Bearer X-Glean-Auth-Type: OAUTH ``` ``` Authorization: Bearer ``` ``` Authorization: Bearer X-Glean-ActAs: user@company.com ``` *** ## Quick Start[​](#quick-start "Direct link to Quick Start") 1 Choose your authentication method Use the decision guide above to pick OAuth or Glean tokens 2 Follow the detailed setup guide * **OAuth**: [Complete OAuth Setup Guide](/api-info/client/authentication/oauth.md) * **Glean Tokens**: [Complete Glean Tokens Guide](/api-info/client/authentication/glean-issued.md) 3 Test your integration Use the test commands in your chosen guide to verify authentication 4 Build your application Start building with the [Client API Reference](/api/client-api.md) *** ## Testing Your Authentication[​](#testing-your-authentication "Direct link to Testing Your Authentication") Use the [Search API Explorer](/api/client-api/search/search.md) to test a Client API request against your own tenant. API Explorer tests Client API authentication only. It is not production sample code. Implement the OAuth flow and token handling in your application before you deploy. ### Quick Test Commands[​](#quick-test-commands "Direct link to Quick Test Commands") Replace `` with your Glean instance name ([how to find](/get-started/authentication.md#finding-your-server-url)): * Glean OAuth Authorization Server * External IdP OAuth * User-Scoped Token * Global Token ``` curl -X POST https://-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"query": "test", "pageSize": 1}' ``` ``` curl -X POST https://-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'X-Glean-Auth-Type: OAUTH' \ -H 'Content-Type: application/json' \ -d '{"query": "test", "pageSize": 1}' ``` ``` curl -X POST https://-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"query": "test", "pageSize": 1}' ``` ``` curl -X POST https://-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'X-Glean-ActAs: your-email@company.com' \ -H 'Content-Type: application/json' \ -d '{"query": "test", "pageSize": 1}' ``` ### Expected Response[​](#expected-response "Direct link to Expected Response") Successful authentication returns a 200 status with search results: ``` { "results": [...], "trackingToken": "...", "requestId": "..." } ``` *** ## Common Authentication Errors[​](#common-authentication-errors "Direct link to Common Authentication Errors") | Error | Likely Cause | Solution | | ----------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` | Invalid or expired token | Verify token is correct and not expired | | `403 Forbidden` | Insufficient permissions | Check token scopes or OAuth settings | | `Missing X-Glean-Auth-Type header` / `Invalid Secret` | External-IdP OAuth token sent without the auth-type header | Add `X-Glean-Auth-Type: OAUTH` (external-IdP tokens only; not needed for Glean Authorization Server tokens) | | `Required header missing: X-Glean-ActAs` | Global token header missing | Add `X-Glean-ActAs: user@email.com` | For detailed troubleshooting, see your specific authentication guide. *** ## Best Practices[​](#best-practices "Direct link to Best Practices") ### Security[​](#security "Direct link to Security") * **Store tokens securely** - Never commit tokens to version control * **Use environment variables** for token storage in applications * **Implement proper error handling** for authentication failures * **Monitor token usage** through appropriate admin consoles ### Development[​](#development "Direct link to Development") * **Test authentication first** - Verify auth works before building features * **Create separate tokens** for development, staging, and production * **Use descriptive names** for tokens to track their purpose * **Plan your architecture** early - some settings cannot be changed later *** ## Implementation Guides[​](#implementation-guides "Direct link to Implementation Guides") Ready to implement? Choose your path: ### [OAuth Authentication](/api-info/client/authentication/oauth.md) [**Complete OAuth setup guide**](/api-info/client/authentication/oauth.md) [* Provider-specific setup (Google, Azure, Okta, OneLogin)* Configuration in Glean admin console* Implementation examples and testing* OAuth-specific troubleshooting](/api-info/client/authentication/oauth.md) ### [Glean-Issued Tokens](/api-info/client/authentication/glean-issued.md) [**Complete Glean tokens guide**](/api-info/client/authentication/glean-issued.md) [* Token creation and management* Scope selection and planning* User-scoped vs global tokens* Token-specific troubleshooting](/api-info/client/authentication/glean-issued.md) ### [Datasource OAuth Redirects](/api-info/client/authentication/datasource-oauth.md) [**Control post-auth redirects**](/api-info/client/authentication/datasource-oauth.md) [* Per-request redirect URL for headless integrations* Tenant-level default redirect configuration* Full flow walkthrough](/api-info/client/authentication/datasource-oauth.md) *** ## Next Steps[​](#next-steps "Direct link to Next Steps") ### [Client API Reference](/api/client-api.md) [Explore available Client API endpoints](/api/client-api.md) ### [Search Implementation](/guides/search/filtering-results.md) [Learn advanced search techniques](/guides/search/filtering-results.md) ### [Chat Integration](/api/client-api/chat/overview.md) [Build chat applications with Glean](/api/client-api/chat/overview.md) *** ## Need Help?[​](#need-help "Direct link to Need Help?") * **Getting Started**: Use the decision guide above to choose your path * **Technical Issues**: Check the detailed guides linked above * **Admin Access**: Contact your Glean administrator for token creation or OAuth setup * **Community**: Join discussions at [community.glean.com](https://community.glean.com) --- # Client API Usage Guide Learn the core concepts and patterns for building applications with Glean's Client API. This guide focuses on understanding what you can build and how the API works, rather than language-specific implementation details. **Choose Your SDK** Ready to start coding? Check out our [official API clients](/libraries/api-clients.md) for Python, TypeScript, Go, and Java with complete examples and installation instructions. ## Core APIs[​](#core-apis "Direct link to Core APIs") ### Chat API - Conversational AI[​](#chat-api---conversational-ai "Direct link to Chat API - Conversational AI") Ask questions and get AI-powered answers grounded in your organization's knowledge. **Request Format:** ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/chat \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "messages": [ { "author": "USER", "fragments": [ { "text": "What is our vacation policy?" } ] } ] }' ``` **Response Structure:** ``` { "messages": [ { "messageType": "CONTENT", "author": "GLEAN_AI", "fragments": [ { "text": "Based on our employee handbook, the vacation policy allows..." }, { "citation": { "sourceDocument": { "title": "Employee Handbook 2024", "url": "https://company.com/handbook", "snippet": "Employees accrue 15 days of PTO annually..." } } } ] } ], "chatId": "chat_abc123", "followUpPrompts": [ "What are the paid time off policies?", "How do I request vacation time?" ], "backendTimeMillis": 1200, "chatSessionTrackingToken": "session_xyz789" } ``` **Key Concepts:** * **Fragments**: Mix of text content and citations in responses * **Chat ID**: Use `chatId` for follow-up messages in the same conversation * **Citations**: Source documents that informed the AI's response (inline in fragments) * **Follow-up Prompts**: Suggested questions based on the response context * **Tracking Tokens**: For analytics and feedback reporting * **Streaming**: Set `"stream": true` for real-time response chunks * **Multi-turn**: Include previous messages for conversational context ### Search API - Enterprise Search[​](#search-api---enterprise-search "Direct link to Search API - Enterprise Search") Find documents, people, and information across all connected systems. **Request Format:** ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "quarterly planning", "pageSize": 10, "requestOptions": { "datasourcesFilter": ["confluence", "gdrive"], "facetBucketSize": 100 } }' ``` **Response Structure:** ``` { "results": [ { "title": "Q4 Planning Document", "url": "https://company.com/q4-plan", "snippets": [ { "snippet": "Our Q4 objectives focus on...", "mimeType": "text/plain" } ], "metadata": { "datasource": "confluence", "documentId": "doc_123", "createTime": "2024-01-15T10:30:00Z", "updateTime": "2024-01-15T10:30:00Z", "author": { "name": "Jane Smith" } } } ], "facetResults": [ { "sourceName": "datasource", "operatorName": "SelectMultiple", "buckets": [ { "value": { "stringValue": "confluence" }, "count": 15 }, { "value": { "stringValue": "gdrive" }, "count": 8 } ] } ], "trackingToken": "...", "requestID": "req_abc123" } ``` **Key Concepts:** * **Results**: Array of documents with rich metadata * **Facets**: Aggregations for filtering and navigation, returned as a `facetResults` array (e.g., by datasource, author) * **Filters**: Narrow results by datasource, author, date, etc. * **Ranking**: Results ordered by relevance scoring ### Agents API - Pre-built Workflows[​](#agents-api---pre-built-workflows "Direct link to Agents API - Pre-built Workflows") Execute specialized AI agents built in Glean's Agent Builder for complex workflows. **Required Scopes:** `agents`, `chat` **Request Format:** ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/agents/runs/wait \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "agent_id": "your-agent-id", "input": { "query": "Analyze our Q4 sales performance", "region": "North America", "timeframe": "Q4 2023" } }' ``` **Response Structure:** ``` { "run": { "agent_id": "your-agent-id", "input": { "query": "Analyze our Q4 sales performance", "region": "North America", "timeframe": "Q4 2023" }, "status": "success" }, "messages": [ { "role": "GLEAN_AI", "content": [ { "text": "Based on the Q4 sales data, I found that sales increased 15% compared to Q3...", "type": "text" } ] } ] } ``` **Key Concepts:** * **Agent ID**: Unique identifier for pre-built agents in Agent Builder * **Input vs Messages**: Use `input` object for data OR `messages` array for conversation * **Run Status**: "success" or "error" indicating execution result * **Response Messages**: Agent responses in message format with role and content * **Wait vs Stream**: Use `/runs/wait` for complete response or `/runs/stream` for real-time ## Common Integration Patterns[​](#common-integration-patterns "Direct link to Common Integration Patterns") ### Pattern 1: Content Discovery[​](#pattern-1-content-discovery "Direct link to Pattern 1: Content Discovery") Use search to help users discover and navigate to specific documents: 1 Initial Discovery Search Start with a broad search to find relevant content: ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "employee onboarding", "pageSize": 10, "requestOptions": { "facetBucketSize": 100 } }' ``` 2 Present Results with Navigation Show users the search results with links to actual documents, plus facets for further filtering: * Display document titles, snippets, and direct links * Show facet counts (e.g., "Confluence (15)", "Google Drive (8)") * Allow users to click through to read full documents 3 Follow-up Questions Once users have context from reading documents, they can ask specific questions via chat: ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/chat \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "messages": [ { "author": "USER", "fragments": [ { "text": "What are the steps for setting up a new employee'\''s laptop?" } ] } ] }' ``` ### Pattern 2: Progressive Conversations[​](#pattern-2-progressive-conversations "Direct link to Pattern 2: Progressive Conversations") Build multi-turn conversations that maintain context: ``` # First message curl -X POST https://your-instance-be.glean.com/rest/api/v1/chat \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "messages": [ { "author": "USER", "fragments": [{ "text": "What are our company'\''s main products?" }] } ] }' # Follow-up using chatId curl -X POST https://your-instance-be.glean.com/rest/api/v1/chat \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "chatId": "chat_abc123", "messages": [ { "author": "USER", "fragments": [{ "text": "What are our company'\''s main products?" }] }, { "author": "GLEAN_AI", "fragments": [{ "text": "Our main products include..." }] }, { "author": "USER", "fragments": [{ "text": "Which one generates the most revenue?" }] } ] }' ``` ### Pattern 3: Faceted Search Navigation[​](#pattern-3-faceted-search-navigation "Direct link to Pattern 3: Faceted Search Navigation") Use facets to help users navigate and filter results: 1 Initial Search Start with a broad search to understand available content: ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "project planning", "pageSize": 10, "requestOptions": { "facetBucketSize": 100 } }' ``` 2 Show Facets Display facet counts to users (e.g., "Confluence (15)", "Google Drive (8)") 3 Filtered Search Let users click facets to refine results: ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "project planning", "pageSize": 10, "requestOptions": { "datasourcesFilter": ["confluence"], "facetFilters": [ { "fieldName": "author", "values": ["Jane Smith"] } ], "facetBucketSize": 100 } }' ``` ### Pattern 4: Streaming for Real-time UX[​](#pattern-4-streaming-for-real-time-ux "Direct link to Pattern 4: Streaming for Real-time UX") Use streaming for interactive chat experiences: ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/chat \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "stream": true, "messages": [ { "author": "USER", "fragments": [{ "text": "Explain our Q4 strategy" }] } ] }' ``` Handle the streaming response to show text as it arrives, creating a more engaging user experience. ## What You Can Build[​](#what-you-can-build "Direct link to What You Can Build") ### Knowledge Chatbots Build conversational interfaces that answer questions using your organization's content ### Search Applications Create custom search experiences with filtering, faceting, and result customization ### Content Discovery Help users find relevant documents and experts across your organization ### Workflow Automation Use agents to automate complex business processes and analysis ## Implementation Resources[​](#implementation-resources "Direct link to Implementation Resources") ### [Official API Clients](/libraries/api-clients.md) [Python, TypeScript, Go, and Java SDKs with complete examples](/libraries/api-clients.md) ### [Complete Examples](/guides/chat/chatbot-example.md) [Step-by-step tutorials for building real applications](/guides/chat/chatbot-example.md) ### [Authentication Setup](/api-info/client/authentication/overview.md) [How to get and manage API tokens](/api-info/client/authentication/overview.md) ### [Advanced Search](/guides/search/filtering-results.md) [Master filtering, faceting, and result customization](/guides/search/filtering-results.md) **Ready to Start Coding?** Check out our [API clients documentation](/libraries/api-clients.md) for your preferred programming language, complete with installation instructions and working examples. --- # Choose a retrieval interface Glean exposes enterprise knowledge through two main retrieval interfaces: **REST APIs** and the **Glean MCP server**. Choosing the right one depends on what you are building. ## Glean REST APIs[​](#glean-rest-apis "Direct link to Glean REST APIs") The Glean REST APIs give you direct, programmatic access to Glean functionality. You control exactly what to query, when to query it, and how to handle the response. Use the REST APIs when you are building: * A custom search experience * A backend pipeline that processes or aggregates Glean data * An integration that needs deterministic, structured results ## Glean MCP server[​](#glean-mcp-server "Direct link to Glean MCP server") The Glean MCP server exposes Glean's capabilities as tools that an LLM can invoke. The model decides when to call a tool and what to send. The MCP server includes tools for search, chat, document retrieval, code search, people lookup, and more. Use MCP when you are building: * An LLM-based application or agent that needs to retrieve enterprise context * A chat experience backed by Glean's knowledge graph * Any integration where the LLM, not your code, drives retrieval ## Example: retrieving context for an LLM[​](#example-retrieving-context-for-an-llm "Direct link to Example: retrieving context for an LLM") A common mistake is calling the Search REST API from an LLM-based application. The Search API expects a deterministic query and returns structured results — it is not designed for the variable, rewritten queries an LLM produces. For LLM applications, use the MCP tools instead: | Goal | Use | | -------------------------------------------- | ------------------------------------------------------ | | Retrieve documents for an LLM to reason over | Glean MCP `search` tool — send the LLM-rewritten query | | Build an end-to-end chat experience | Glean MCP `chat` tool — send the original user query | | Build a custom search UI | Search REST API — send the user query directly | info For a full list of available MCP tools, see the [MCP documentation](https://docs.glean.com/administration/platform/mcp/about). --- # Client API Quickstart Make a permission-aware search request with the Client API. This is the shortest path from a credential to a successful response; use the [Client API overview](/api/client-api.md) to choose other capabilities. ## Before you begin[​](#before-you-begin "Direct link to Before you begin") 1 Find your API URL If automatic discovery is unavailable, ask a Glean admin for the complete **Server instance (QE)** value. Glean admins can copy it from the [About Glean page](https://app.glean.com/admin/about-glean). 2 Configure authentication Configure OAuth or a user-scoped Glean-issued token with the [Client API authentication guide](/api-info/client/authentication/overview.md). 3 Choose a test query Use a query that should return content the authenticated user can access. ## Make your first request[​](#make-your-first-request "Direct link to Make your first request") Replace `` and any placeholder API URL, then run the request: ``` curl -X POST https://your-instance-be.glean.com/rest/api/v1/search \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "quarterly planning", "pageSize": 10, "requestOptions": { "datasourcesFilter": ["confluence", "gdrive"], "facetBucketSize": 100 } }' ``` A successful response contains a `results` array with documents the authenticated user is allowed to see. An empty `results` array is still a successful request; try a broader query or confirm that the user can access matching content. See the [Search API reference](/api/client-api/search/overview.md) for request parameters and the complete response model. ## Next steps[​](#next-steps "Direct link to Next steps") ### [Authentication](/api-info/client/authentication/overview.md) [Review Client API credential types and user context.](/api-info/client/authentication/overview.md) ### [Usage Guide](/api-info/client/getting-started/basic-usage.md) [Explore Chat, Search, Agents, and common integration patterns.](/api-info/client/getting-started/basic-usage.md) ### [API Reference](/api/client-api.md#client-api-reference) [Browse Client API endpoints by capability.](/api/client-api.md#client-api-reference) --- # Indexing API Authentication This guide covers implementing authentication for Glean's **Indexing API**, which powers document indexing, datasource management, and administrative operations. The Indexing API **only supports Glean-issued tokens** - OAuth authentication is not available. warning **Critical**: The Indexing API does **NOT support OAuth authentication**. You must use Glean-issued tokens for all Indexing API operations. ### Glean-Issued Tokens (Only Option) **Manually created through admin console** * **Authentication**: Glean tokens only (no OAuth support) * **Permissions**: Global or limited to specific datasource apps * **Access**: Standard Indexing API operations; Custom Metadata uses separately scoped credentials * **Advanced Features**: IP restrictions, token rotation, custom expiry * **Use Cases**: Document indexing, datasource management, bulk operations *** ## Authentication Headers Reference[​](#authentication-headers-reference "Direct link to Authentication Headers Reference") The Indexing API uses a simple authentication header format since it only supports Glean-issued tokens: ``` Authorization: Bearer ``` ### Header Details[​](#header-details "Direct link to Header Details") | Header | Required | Description | Example Value | | --------------- | -------- | ----------------------------------------- | ------------------------ | | `Authorization` | Always | Bearer token with your Indexing API token | `Bearer glean_XYZ123...` | note Unlike the Client API, Indexing API tokens do **not** require additional headers like `X-Glean-Auth-Type` or `X-Glean-ActAs`. *** ## Quick Setup Overview[​](#quick-setup-overview "Direct link to Quick Setup Overview") 1 Admin Access Required **Setup Admins**, **Admins**, and **Super Admins** can create Indexing API tokens (only Super Admins can grant `global` scope). **API Token Creators** can create tokens scoped to themselves only. 2 Navigate to Token Management Go to [Indexing API Settings](https://app.glean.com/admin/platform/tokenManagement?tab=indexing) 3 Create New Token Configure token with optional IP restrictions and rotation settings 4 Use Token in Requests Include `Authorization: Bearer ` header in all Indexing API calls *** ## Creating Indexing API Tokens[​](#creating-indexing-api-tokens "Direct link to Creating Indexing API Tokens") ### Prerequisites[​](#prerequisites "Direct link to Prerequisites") * **Super Admin** or **API Token Creator** access to Glean's admin console * **Datasource configured** in Glean (for document indexing) * **IP ranges identified** (if using IP restrictions) ### Token Creation Process[​](#token-creation-process "Direct link to Token Creation Process") 1 Navigate to Indexing Tokens Go to [Glean Admin Console](https://app.glean.com/admin/platform/tokenManagement?tab=indexing) → **Platform** → **API Tokens** → **Indexing Tokens** tab 2 Add New Token Click **"Add API token"** and configure: ![Add new Indexing API token](/img/authentication/add-indexing-api-token.png)![Add new Indexing API token](/img/authentication/add-indexing-api-token-dark.png) * **Token Name**: Descriptive name for tracking * **Global Permissions**: Full indexing API access * **App Permissions**: Limit to specific datasources (optional) * **Expires On**: Expiration date (**required**) — the Admin Console validates this field before creating the token * **IP Restrictions**: Optional IP range limitations * **Rotation Settings**: Optional automated rotation note An expiration date is **required**. If you leave the **Expires** field empty, the Admin Console blocks token creation with a validation message rather than failing with a server error. 3 Save Token Securely warning The token is only displayed once after creation. Save it securely - you cannot retrieve it again. ![Token creation success](/img/authentication/create-token-success.png)![Token creation success](/img/authentication/create-token-success-dark.png) ### Token Properties[​](#token-properties "Direct link to Token Properties") * **API Support**: **Indexing API only** (Client API not supported) * **User Context**: Service-level access rather than Client API user impersonation * **Access**: Global or limited to selected datasource apps; [Custom Metadata](/api-info/indexing/custom-metadata/authentication.md) uses separate global or group-specific scopes * **Expiration**: Required expiry date (validated in the Admin Console at creation time) * **Security**: Support for IP restrictions and rotation *** ## Advanced Token Features[​](#advanced-token-features "Direct link to Advanced Token Features") ### IP Address Restrictions[​](#ip-address-restrictions "Direct link to IP Address Restrictions") Restrict token usage to specific IP ranges for enhanced security: IP Restrictions Setup **Configure IP Ranges** 1. During token creation, specify **Greenlisted IPs** 2. Use comma-separated list of CIDR format ranges 3. Example: `192.168.1.0/24,10.0.0.0/16` ![IP restrictions configuration](/img/authentication/greenlisted-ips.png)![IP restrictions configuration](/img/authentication/greenlisted-ips-dark.png) **CIDR Format Examples** | CIDR Notation | Description | IP Range | | ---------------- | ------------- | --------------------------- | | `192.168.1.0/24` | Single subnet | 192.168.1.1 - 192.168.1.254 | | `10.0.0.0/16` | Large network | 10.0.0.1 - 10.0.255.254 | | `203.0.113.5/32` | Single IP | 203.0.113.5 only | **Benefits** * Restrict API access to known infrastructure * Prevent unauthorized usage if token is compromised * Meet compliance requirements for IP-based access control ### Token Rotation[​](#token-rotation "Direct link to Token Rotation") Enable automatic token rotation for enhanced security: Token Rotation Setup **Configure Rotation** 1. During token creation, set **Rotation Period** (in minutes) 2. Use the rotation API endpoint to rotate tokens programmatically 3. Minimum rotation period: 1440 minutes (24 hours) ![Token rotation configuration](/img/authentication/rotation-period.png)![Token rotation configuration](/img/authentication/rotation-period-dark.png) **Using the Rotation API** The token to rotate is identified by the bearer token in the request, so no request body is required. The current token must be the one being rotated. ``` curl -X POST https://instance-be.glean.com/api/index/v1/rotatetoken \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' ``` **Response** ``` { "rawSecret": "new-rotated-token-value", "createdAt": 1735689599, "rotationPeriodMinutes": 1440 } ``` **Best Practices** * Implement rotation in your application logic * Store both old and new tokens during rotation period * Test rotation in development environment first *** ## Using Indexing API Tokens[​](#using-indexing-api-tokens "Direct link to Using Indexing API Tokens") ### Authentication Header[​](#authentication-header "Direct link to Authentication Header") All Indexing API requests require a single authentication header: ``` Authorization: Bearer ``` ### Example Requests[​](#example-requests "Direct link to Example Requests") Replace `instance` with your Glean instance name ([how to find](/get-started/authentication.md#finding-your-server-url)): * Index Document * Bulk Index Documents * Get Datasource Config * Add Datasource ``` curl -X POST https://instance-be.glean.com/api/index/v1/indexdocument \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "datasource": "my-datasource", "document": { "id": "doc-123", "title": "Example Document", "body": {"mimeType": "text/plain", "textContent": "Document content"}, "updatedAt": "2024-01-15T10:30:00Z" } }' ``` ``` curl -X POST https://instance-be.glean.com/api/index/v1/bulkindexdocuments \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "datasource": "my-datasource", "documents": [ { "id": "doc-1", "title": "First Document", "body": {"mimeType": "text/plain", "textContent": "Content 1"} }, { "id": "doc-2", "title": "Second Document", "body": {"mimeType": "text/plain", "textContent": "Content 2"} } ] }' ``` ``` curl -X POST https://instance-be.glean.com/api/index/v1/getdatasourceconfig \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "datasource": "my-datasource" }' ``` ``` curl -X POST https://instance-be.glean.com/api/index/v1/adddatasource \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "datasource": { "name": "My Custom Datasource", "displayName": "Custom Data Source", "homeUrl": "https://example.com" } }' ``` *** ## Testing Your Authentication[​](#testing-your-authentication "Direct link to Testing Your Authentication") ### Quick Verification[​](#quick-verification "Direct link to Quick Verification") Test your token with a read-only datasource config request: ``` curl -X POST https://-be.glean.com/api/index/v1/getdatasourceconfig \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "datasource": "my-datasource" }' ``` ### Expected Response[​](#expected-response "Direct link to Expected Response") Successful authentication returns your datasource's configuration: ``` { "name": "my-datasource", "displayName": "My Datasource", "urlRegex": "https://internal.example.com/.*" } ``` ### Test Document Indexing[​](#test-document-indexing "Direct link to Test Document Indexing") Test document indexing with a minimal document: ``` curl -X POST https://-be.glean.com/api/index/v1/indexdocument \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "datasource": "test-datasource", "document": { "id": "test-doc-001", "title": "Test Document", "body": { "mimeType": "text/plain", "textContent": "This is a test document for authentication verification." }, "updatedAt": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'" } }' ``` *** ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") ### Common Authentication Errors[​](#common-authentication-errors "Direct link to Common Authentication Errors") | Error | Cause | Solution | | -------------------------------------- | ------------------------------- | -------------------------------------------------- | | `401 Unauthorized` | Invalid or expired token | Verify token is correct and not expired | | `403 Forbidden - IP not allowed` | Request from non-whitelisted IP | Add your IP to token's allowed IP ranges | | `400 Bad Request - Invalid datasource` | Datasource doesn't exist | Create datasource first or verify name | | `413 Request Entity Too Large` | Document/request too large | Split into smaller requests or use bulk operations | | `429 Too Many Requests` | Rate limit exceeded | Implement exponential backoff retry logic | | `500 Internal Server Error` | Glean service issue | Check Glean status page, retry with backoff | ### Debugging Steps[​](#debugging-steps "Direct link to Debugging Steps") 1 Verify token validity * Check that token hasn't expired * Ensure token was created for Indexing API (not Client API) * Test with a simple read-only endpoint like datasource config 2 Check IP restrictions * Verify your IP is in allowed ranges (if IP restrictions enabled) * Test from different IP if needed * Check CIDR format in token configuration 3 Validate request format * Ensure Content-Type header is set correctly * Verify JSON payload is valid * Check required fields are present 4 Test with minimal request * Start with datasource config or datasource status endpoints * Use minimal document for indexing tests * Gradually add complexity once basic auth works ### Indexing-Specific Issues[​](#indexing-specific-issues "Direct link to Indexing-Specific Issues") Document Indexing Troubleshooting **Common Document Indexing Issues** | Issue | Cause | Solution | | -------------------------------- | ----------------------- | --------------------------------------------- | | Document not appearing in search | Processing delay | Wait 5-10 minutes, check document status API | | Permissions error | User lacks access | Verify document permissions are set correctly | | Invalid MIME type | Unsupported file format | Check supported MIME types documentation | | Document too large | Size exceeds limits | Split document or compress content | **Validation Steps** 1. **Check document status**: Use the document status API to verify indexing completion 2. **Verify permissions**: Ensure document permissions allow intended users access 3. **Test search**: Search for indexed document using specific terms from content 4. **Monitor logs**: Check Glean's admin console for indexing errors *** ## Best Practices[​](#best-practices "Direct link to Best Practices") ### Security[​](#security "Direct link to Security") * **Store tokens securely** - never commit tokens to version control * **Use IP restrictions** when possible to limit token usage * **Enable token rotation** for long-running applications * **Set an appropriate expiration date** - expiration is required, so choose the shortest window that fits your use case (and plan to rotate or reissue before it lapses) * **Monitor token usage** through the admin console * **Create separate tokens** for different environments (dev/staging/prod) ### Performance[​](#performance "Direct link to Performance") * **Use bulk operations** when indexing multiple documents * **Implement retry logic** with exponential backoff * **Monitor rate limits** and adjust request frequency * **Batch requests** efficiently to reduce API calls * **Use appropriate content chunking** for large documents ### Development[​](#development "Direct link to Development") * **Test thoroughly** in development environment first * **Use descriptive token names** to track purpose and usage * **Document token configuration** for team members * **Plan for token rotation** in application architecture * **Implement proper error handling** for all API responses ### Operational[​](#operational "Direct link to Operational") * **Monitor indexing status** regularly through admin console * **Set up alerts** for indexing failures or errors * **Track document counts** to verify successful indexing * **Regular token audits** to remove unused tokens * **Backup token configurations** for disaster recovery *** ## Next Steps[​](#next-steps "Direct link to Next Steps") ### [Setup Datasource](/api-info/indexing/getting-started/setup-datasource.md) [Configure your first datasource for document indexing](/api-info/indexing/getting-started/setup-datasource.md) ### [Index Documents](/api-info/indexing/getting-started/index-documents.md) [Learn document indexing best practices](/api-info/indexing/getting-started/index-documents.md) ### [Indexing API Reference](/api/indexing-api/documents-overview.md) [Explore all Indexing API endpoints](/api/indexing-api/documents-overview.md) --- # Token Rotation This guide covers token rotation for the indexing API. You'll learn how to exchange your rotatable access token for a new access token. With token rotation, you'll provide an extra layer of security for your access tokens. To use the rotate token endpoint, you must first create a rotatable token from the admin console by specifying a rotation period in minutes. Please refer to [Managing API Tokens](/api-info/indexing/authentication/overview.md) documentation on how to create a rotatable token. ## Rotate token endpoint `/rotatetoken`[​](#rotate-token-endpoint-rotatetoken "Direct link to rotate-token-endpoint-rotatetoken") Here is a sample request to the `/rotatetoken` endpoint. ``` curl -X POST https://customer-be.glean.com/api/index/v1/rotatetoken \ -H 'Authorization: Bearer ' ``` To rotate a token using the `/rotatetoken` endpoint, you must send the token you want to rotate as the bearer token in the request. This will create and return a new token with the same properties as the parent token, including the rotation period minutes property, which cannot be changed. The response will include the new token's raw secret, creation time, and rotation period in minutes. ### Sample response[​](#sample-response "Direct link to Sample response") ``` { "rawSecret": "new-secret", "createdAt": 16400000, "rotationPeriodMinutes": 3600 } ``` ## Rotation period[​](#rotation-period "Direct link to Rotation period") After creating a rotatable token in the admin console, it is important to regularly rotate the token using the /rotatetoken endpoint to prevent it from expiring. The rotation period is specified when the token is created and cannot be changed. To ensure the token is rotated within the specified period, we recommend setting up a cron job that runs at a frequency less than the rotation period. Following is a sample structure of how the cron job might look like -: ``` def rotate_token(): # Make a POST request to the /rotatetoken endpoint response = requests.post('http://domain-be.glean.com/api/index/v1/rotatetoken') # Extract the new token and rotation period from the response data = response.json() new_secret = data['rawSecret'] rotation_period_minutes = data['rotationPeriodMinutes'] created_at = data['createdAt'] # Store the new token and rotation period in the database store_in_db(new_secret, rotation_period_minutes, created_at) # Start using the new token for all future API calls set_api_token(new_secret) # Set up the cron job to run the rotate_token function every rotationPeriodMinutes minutes def create_cron_job(rotationPeriodMinutes): cron = CronTab(user='username') job = cron.new(command='/path/to/script.py') job.minute.every(rotationPeriodMinutes) cron.write() ``` ## Some things to note[​](#some-things-to-note "Direct link to Some things to note") * When a token is rotated, it cannot be used to make any requests after a buffer period of 10 minutes. This buffer period is not configurable. * If the token expires, it cannot be used to make indexing API requests. You would need to create a new token from the admin console. * It is recommended that tokens are stored in an encrypted form. * It is important to note that the token will still expire at the expiry time specified during token creation, even if it is being rotated regularly. --- # Authentication Custom Metadata requests require a Bearer token in the `Authorization` header. Tokens are **scoped**, so metadata-management permissions can be delegated independently of full indexing-token access. ## Token scopes[​](#token-scopes "Direct link to Token scopes") When creating a token via the [indexing token creation flow](/api-info/indexing/authentication/overview.md), set the scope to one of: * **Global scope** — `custommetadata:global_scope`. Manages schemas and metadata for **any** group. * **Group-specific scope** — `custommetadata:`. Manages the schema and metadata for the named group only. A group-scoped token can manage metadata for that group across any document in Glean — it is not tied to a particular datasource. ## Choosing a scope[​](#choosing-a-scope "Direct link to Choosing a scope") | Scenario | Recommended scope | | ---------------------------------------------------------------------- | ----------------------------------------------- | | One pipeline managing several metadata groups | `custommetadata:global_scope` | | Per-team or per-integration metadata pipelines that should be isolated | `custommetadata:` | | Third-party or partner integrations | `custommetadata:` (least privilege) | ## Using the token[​](#using-the-token "Direct link to Using the token") Include the token as a Bearer credential on every request: ``` curl -H 'Authorization: Bearer ' \ https://-be.glean.com/rest/api/index/custom-metadata/schema/ ``` Requests made with a token whose scope does not cover the target group return `401 Unauthorized`. --- # Custom Properties vs Custom Metadata Glean offers two ways to attach structured metadata to documents for search, filtering, and faceting: 1. **[Custom Properties](/api-info/indexing/datasource/custom-properties.md)** — properties defined on a custom datasource and set on each document via the Indexing API's `/indexdocument` endpoint. 2. **[Custom Metadata API](/api-info/indexing/custom-metadata/overview.md)** — a standalone API that attaches metadata to documents already indexed in Glean, regardless of which datasource they came from. The most important distinction: info **Custom Metadata works with any datasource** — Google Drive, Confluence, Jira, custom datasources you push via the Indexing API, and every other connector indexed in Glean. Custom Properties are limited to documents you index yourself through a custom datasource. ## At a glance[​](#at-a-glance "Direct link to At a glance") | | Custom Properties (Indexing API) | Custom Metadata API | | -------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | | **Works with** | Documents from a custom datasource only | Any document in Glean — native connectors (Google Drive, Confluence, Jira, etc.) **and** custom datasources | | **Purpose** | Attach metadata to documents you index into Glean | Enrich documents already indexed in Glean, without re-indexing | | **Schema scope** | Per-datasource | Per metadata group (namespace) | | **Update mechanism** | Re-upload the entire document | `PUT` metadata for a single `(document, group)` pair | | **UI configuration** | Full support — facet ordering, display labels, search-result rendering (`uiOptions`) | Not supported — no UI display options | | **Property types** | `TEXT`, `PICKLIST`, `TEXTLIST`, `MULTIPICKLIST`, `INT`, `DATE`, `USERID` | `TEXT`, `PICKLIST`, `TEXTLIST`, `MULTIPICKLIST` | | **Access control** | Tied to the indexing token for the datasource | Scoped tokens per metadata group (`custommetadata:`) | ## When to use Custom Properties[​](#when-to-use-custom-properties "Direct link to When to use Custom Properties") Use [Custom Properties](/api-info/indexing/datasource/custom-properties.md) when you **own the datasource** and are already pushing documents to Glean via the Indexing API. Typical scenarios: * **Custom connector with rich metadata.** Your connector pushes documents along with structured metadata from the same source system (e.g., department, priority, document type). * **UI-driven faceting and display.** You need control over how metadata appears in the Glean UI — facet ordering, display labels, rendering metadata in search results. * **Metadata and content share a lifecycle.** When the document is updated, its metadata is typically updated at the same time. * **Numeric or date fields.** Custom Properties support `INT`, `DATE`, and `USERID` types; Custom Metadata does not. ### Limitation[​](#limitation "Direct link to Limitation") Updating a custom property requires re-uploading the entire document via `/indexdocument`. Every call must include the full document definition (content, permissions, and all properties). This makes metadata-only updates expensive for large documents or high-frequency changes. ## When to use the Custom Metadata API[​](#when-to-use-the-custom-metadata-api "Direct link to When to use the Custom Metadata API") Use the [Custom Metadata API](/api-info/indexing/custom-metadata/overview.md) to enrich documents that already exist in Glean — **including documents from any native connector** — without re-indexing. Typical scenarios: * **Cross-datasource enrichment.** Tag Google Drive documents with CRM data, label Confluence pages with project codes from Jira, etc. * **Native-connector enrichment.** Add metadata to documents from any connector — Google Drive, Confluence, Jira, GitHub, Salesforce, ServiceNow, and so on. You don't need to own or operate the underlying datasource. * **Decoupled metadata ownership.** The team managing metadata is different from the team that owns the document source. * **High-frequency metadata updates.** Metadata changes often but document content does not — avoid re-uploading entire documents. * **Tagging and classification.** Apply labels like "reviewed", "compliance-approved", or "deprecated" to existing documents. * **Lightweight integrations.** Attach structured data to documents without building a full custom connector. ### Limitation[​](#limitation-1 "Direct link to Limitation") The Custom Metadata API does not currently support UI configuration options such as facet ordering, display labels, or search-result rendering. Metadata is usable for search and faceted filtering, but its presentation in the Glean UI cannot be controlled. If you need this, use Custom Properties on a custom datasource. ## Decision guide[​](#decision-guide "Direct link to Decision guide") ``` Is the metadata coming from the same source as the document content? │ ├─ YES → Are you already pushing these documents via the Indexing API? │ │ │ ├─ YES → Do you need UI options (facet ordering, display │ │ names, search-result rendering) or INT/DATE types? │ │ │ │ │ ├─ YES → Use Custom Properties │ │ │ │ │ └─ NO → Either works. Custom Properties are simpler │ │ if you're already re-uploading. Custom Metadata │ │ is better if metadata changes more often than │ │ content. │ │ │ └─ NO → Use the Custom Metadata API │ └─ NO → Use the Custom Metadata API (works with any datasource — native connectors or custom — and enriches existing docs without re-indexing) ``` ### Quick reference[​](#quick-reference "Direct link to Quick reference") | Question | Custom Properties (Indexing API) | Custom Metadata API | | ---------------------------------------------------------------------------------------------- | --------------------------------------------------- | ---------------------------------------------------- | | Can I add metadata to documents from native connectors (Google Drive, Confluence, Jira, etc.)? | No — only documents indexed via a custom datasource | **Yes — any document in Glean, from any datasource** | | Can I update metadata without re-uploading the full document? | No — full document re-upload required | Yes — metadata-only updates | | Can I control facet display names and ordering? | Yes — full UI options support | No — not currently supported | | Can different teams manage metadata independently? | No — tied to the datasource owner | Yes — scoped tokens per group | | Does it support `INT` and `DATE` types? | Yes | No — text-based types only | ## Search behavior[​](#search-behavior "Direct link to Search behavior") From an end-user perspective, both Custom Properties and Custom Metadata are searchable and filterable. * **Faceted search.** Both appear as facets. Custom Properties use `:` syntax. Custom Metadata facet keys include the group namespace: `:`. * **Co-existence.** A document can have both Custom Properties and Custom Metadata simultaneously. They appear alongside each other in faceted search. * **Independent lifecycles.** Re-indexing a document via the Indexing API does not affect Custom Metadata attached to it. The two are managed independently. ## Common patterns[​](#common-patterns "Direct link to Common patterns") The scenarios below are illustrative examples, not exhaustive prescriptions. Many real-world setups combine the two APIs in ways that aren't listed here — use them as a starting point and adapt to your data ownership, update cadence, and UI needs. ### Custom datasource with rich metadata[​](#custom-datasource-with-rich-metadata "Direct link to Custom datasource with rich metadata") **Preferred: Custom Properties** You're building a connector for an internal tool. Documents, their content, and metadata all come from the same source. You want facet display names, ordering, and metadata rendered in search results. Custom Properties fit this case naturally because the metadata ships with the document on every index call. Custom Metadata still works here — it's a fine choice if metadata changes more frequently than content — but Custom Properties give you the UI controls and `INT`/`DATE` types that Custom Metadata lacks. ### Enriching native-connector documents[​](#enriching-native-connector-documents "Direct link to Enriching native-connector documents") **Use: Custom Metadata API** Google Drive docs (or Confluence, Jira, etc.) are already in Glean via the native connector. Your compliance team wants to tag them with review status from an external workflow tool. Custom Properties are not an option here — the documents come from a native connector, not a datasource you control. ### Custom datasource with external enrichment[​](#custom-datasource-with-external-enrichment "Direct link to Custom datasource with external enrichment") **Use: Both** You index documents from your internal wiki via the Indexing API with basic metadata (author, category) as Custom Properties. A separate automation pipeline uses the Custom Metadata API to attach CRM deal-stage data to those same documents. ## Further reading[​](#further-reading "Direct link to Further reading") * [Indexing API Overview](/api-info/indexing/getting-started/overview.md) * [Custom Properties](/api-info/indexing/datasource/custom-properties.md) * [Custom Metadata API](/api-info/indexing/custom-metadata/overview.md) --- # Indexing Metadata Once a [schema is defined](/api-info/indexing/custom-metadata/schema-management.md) for a metadata group, you can attach metadata values to individual documents. ## Endpoints[​](#endpoints "Direct link to Endpoints") | Method | Path | Purpose | | -------- | ----------------------------------------------- | ----------------------------------------------------------- | | `PUT` | `/document/{docId}/custom-metadata/{groupName}` | Add or replace all metadata for a `(docId, groupName)` pair | | `DELETE` | `/document/{docId}/custom-metadata/{groupName}` | Remove all metadata for a `(docId, groupName)` pair | `docId` is the unique Glean identifier of the document. You can obtain it via the Client API (for example, [Search](/api/client-api/search/search.md) or [Get Documents](/api/client-api/documents/getdocuments.md)). ## Add or update metadata[​](#add-or-update-metadata "Direct link to Add or update metadata") ``` PUT /document/{docId}/custom-metadata/{groupName} ``` Indexes custom metadata for the specified document and group. warning This call **replaces** all metadata for the `(docId, groupName)` pair. Any keys not present in the request are removed from the group on that document. To update a single key without losing others, include the full set of keys in the request body. ### Request body[​](#request-body "Direct link to Request body") ``` { "customMetadata": [ { "name": "string", "value": "string" | ["string", ...] } ] } ``` `value` is a string for `TEXT` and `PICKLIST` keys, and an array of strings for `TEXTLIST` and `MULTIPICKLIST` keys. ### Example[​](#example "Direct link to Example") * curl ``` curl -X PUT https://customer-be.glean.com/rest/api/index/document/gdrive_abc123/custom-metadata/compliance \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "customMetadata": [ { "name": "status", "value": "Approved" }, { "name": "reviewDate", "value": "2026-03-15" }, { "name": "tags", "value": ["SOC2", "annual-review"] } ] }' ``` ## Remove metadata[​](#remove-metadata "Direct link to Remove metadata") ``` DELETE /document/{docId}/custom-metadata/{groupName} ``` Removes all metadata for the specified group from the document. Metadata for **other** groups on the same document is unaffected. ## Rate limits[​](#rate-limits "Direct link to Rate limits") | Limit | Value | | -------------- | ------------------------------- | | Sustained rate | 5 requests per second per group | | Burst | Up to 300 requests | Rate limits are enforced **per metadata group**, not per document. Distributing writes across multiple groups increases overall throughput. When the limit is exceeded, the API returns `429 Too Many Requests`. See [Error responses](/api-info/indexing/custom-metadata/overview.md#error-responses) for the full list of status codes. --- # Overview The Custom Metadata API attaches structured metadata to any document already indexed in Glean — regardless of which datasource it came from. Unlike [Custom Properties](/api-info/indexing/datasource/custom-properties.md), which are tied to a custom datasource and require re-uploading the entire document, Custom Metadata can be updated independently and applied to documents from any source (native connectors, custom datasources, etc.). For guidance on which approach fits your use case, see [Custom Properties vs Custom Metadata](/api-info/indexing/custom-metadata/custom-properties-vs-custom-metadata.md). ## Key concepts[​](#key-concepts "Direct link to Key concepts") * **Document ID (`docId`)**: The unique Glean identifier for a document. You can obtain it via the Client API (for example, [Search](/api/client-api/search/search.md) or [Get Documents](/api/client-api/documents/getdocuments.md)). * **Metadata group**: A namespace that groups related metadata keys together. Each group has its own schema and access scope. * **Metadata schema**: Defines the keys allowed in a group, their property types, and indexing behavior. info Metadata group names and key names are case-insensitive and may only contain alphanumeric characters. ## Base URL[​](#base-url "Direct link to Base URL") All Custom Metadata endpoints are served from the indexing backend: ``` https://-be.glean.com/rest/api/index ``` Replace `` with your Glean instance subdomain. ## Workflow at a glance[​](#workflow-at-a-glance "Direct link to Workflow at a glance") A typical end-to-end flow: 1. **[Create a scoped token](/api-info/indexing/custom-metadata/authentication.md).** Generate a token with scope `custommetadata:`, or `custommetadata:global_scope` if you manage multiple groups. 2. **[Define the schema](/api-info/indexing/custom-metadata/schema-management.md).** `PUT /custom-metadata/schema/{groupName}` with the keys you want to track and their property types. 3. **[Attach metadata to documents](/api-info/indexing/custom-metadata/indexing-metadata.md).** `PUT /document/{docId}/custom-metadata/{groupName}` for each document you want to enrich. 4. **[Query](/api-info/indexing/custom-metadata/querying.md).** Use facets (`:`) in Search, or fetch all metadata via `getDocuments` with `includeFields: ["CUSTOM_METADATA"]`. ## Error responses[​](#error-responses "Direct link to Error responses") All Custom Metadata endpoints return errors in this shape: ``` { "error": "", "message": "
" } ``` Common status codes: | Status | Reason | | --------------------------- | ---------------------------------------------------------------------------------------- | | `400 Bad Request` | Invalid request body, unknown property type, or missing required field. | | `401 Unauthorized` | Invalid or missing authentication token, or token scope does not cover the target group. | | `404 Not Found` | The document or metadata group does not exist. | | `409 Conflict` | Schema update conflicts with an existing schema (for example, a property type change). | | `429 Too Many Requests` | Rate limit exceeded for the group. | | `500 Internal Server Error` | Unexpected server-side failure. | --- # Querying Once metadata is attached to documents, it becomes available for search and retrieval through the standard Client API. ## Faceted search[​](#faceted-search "Direct link to Faceted search") Each metadata key becomes a search facet of the form ``. For example, given a group `address` with key `country`, this query returns documents tagged with `USA`: ``` addresscountry:USA ``` Facets follow standard Glean operator syntax and can be combined with other operators, including those produced by [Custom Properties](/api-info/indexing/datasource/custom-properties.md). ## Full-text search[​](#full-text-search "Direct link to Full-text search") When `skipIndexing` is `false` (the default), metadata values participate in full-text search. A document tagged with `status: Approved` matches a free-text query for `Approved`. Setting `skipIndexing: true` keeps the value retrievable on the document but excludes it from full-text matching — useful for internal-only fields you don't want surfacing through generic queries. ## Fetching metadata via Get Documents[​](#fetching-metadata-via-get-documents "Direct link to Fetching metadata via Get Documents") Use the Client API [`getDocuments`](/api/client-api/documents/getdocuments.md) endpoint to fetch all metadata attached to a document: ``` { "ids": ["gdrive_abc123"], "includeFields": ["CUSTOM_METADATA"] } ``` Custom metadata is returned in `Document.metadata.customData`, alongside any datasource-native metadata fields (such as `author`). ## Co-existence with Custom Properties[​](#co-existence-with-custom-properties "Direct link to Co-existence with Custom Properties") A document can have both Custom Properties and Custom Metadata simultaneously. They appear alongside each other in faceted search and `getDocuments` responses, with independent lifecycles — re-indexing a document via the Indexing API does not affect its Custom Metadata. See [Custom Properties vs Custom Metadata](/api-info/indexing/custom-metadata/custom-properties-vs-custom-metadata.md) for a full comparison. --- # Schema Management A metadata group must have a schema defined before metadata can be attached to documents in that group. The schema declares which keys are allowed, what type each key is, and whether the value participates in full-text search. ## Endpoints[​](#endpoints "Direct link to Endpoints") | Method | Path | Purpose | | -------- | ------------------------------------- | --------------------------- | | `PUT` | `/custom-metadata/schema/{groupName}` | Create or update a schema | | `GET` | `/custom-metadata/schema/{groupName}` | Retrieve the current schema | | `DELETE` | `/custom-metadata/schema/{groupName}` | Delete a schema | ## Create or update a schema[​](#create-or-update-a-schema "Direct link to Create or update a schema") ``` PUT /custom-metadata/schema/{groupName} ``` Defines or updates the schema for a metadata group. ### Request body[​](#request-body "Direct link to Request body") ``` { "metadataKeys": [ { "name": "string", "propertyType": "TEXT" | "PICKLIST" | "TEXTLIST" | "MULTIPICKLIST", "skipIndexing": false } ] } ``` | Field | Type | Required | Description | | -------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Key name. Alphanumeric, case-insensitive, max 50 characters. | | `propertyType` | enum | Yes | One of `TEXT`, `PICKLIST`, `TEXTLIST`, `MULTIPICKLIST`. See [Property types](#property-types). | | `skipIndexing` | boolean | No | Default `false`. When `true`, the key is stored on the document but not used for full-text search matching. | ### Example[​](#example "Direct link to Example") * curl ``` curl -X PUT https://customer-be.glean.com/rest/api/index/custom-metadata/schema/compliance \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "metadataKeys": [ { "name": "status", "propertyType": "PICKLIST" }, { "name": "tags", "propertyType": "MULTIPICKLIST" }, { "name": "reviewDate", "propertyType": "TEXT" }, { "name": "internalNotes", "propertyType": "TEXT", "skipIndexing": true } ] }' ``` ## Retrieve a schema[​](#retrieve-a-schema "Direct link to Retrieve a schema") ``` GET /custom-metadata/schema/{groupName} ``` Returns the current schema for the group, in the same shape as the `PUT` request body. ## Delete a schema[​](#delete-a-schema "Direct link to Delete a schema") ``` DELETE /custom-metadata/schema/{groupName} ``` Deletes the schema for a metadata group. Existing metadata values previously attached to documents using this schema are retained until they are removed by a separate cleanup pass. ## Property types[​](#property-types "Direct link to Property types") | Type | Description | Search behavior | | --------------- | -------------------------------------------------------- | --------------------------------------------------------------- | | `TEXT` | Single-line free-form text. | Substring match on punctuation/whitespace boundaries. | | `PICKLIST` | Single value chosen from a small set of distinct values. | Exact-string match; suitable as a facet. | | `TEXTLIST` | Array of free-form text values. | Substring match per entry. | | `MULTIPICKLIST` | Array of picklist values. | Exact-string match per entry; suitable as a multi-select facet. | note `DATE`, `INT`, and `USERID` property types are **not** supported by the Custom Metadata API. ## Schema limits[​](#schema-limits "Direct link to Schema limits") | Limit | Value | | ------------------------- | ------------- | | Maximum metadata groups | 50 | | Maximum keys per group | 20 | | Maximum group name length | 50 characters | | Maximum key name length | 50 characters | --- # Category Choosing the appropriate type enables enhanced ranking. Please contact us if you are unsure of how to categorize your datasource. If a datasource has multiple objectTypes which belong to different categories (such as Zendesk articles and Zendesk tickets), please set each category using `objectDefinitions.docCategory`. The category definitions below still apply. ## Datasource Category[​](#datasource-category "Direct link to Datasource Category") | Category Name | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | KNOWLEDGE\_HUB | Reference documentation that may be continually updated as a source of truth, such as Github READMEs, Confluence documents, or ServiceNow knowledge articles | | PUBLISHED\_CONTENT | Blog posts published at a point in time, such as Brightspot posts or Confluence blog posts. Note that Confluence blog posts are different from other Confluence documents, which should be classified as KNOWLEDGE\_HUB. | | COLLABORATIVE\_CONTENT | Documents that can be edited collaboratively, such as Google Drive, Dropbox, or Figma files | | QUESTION\_ANSWER | Question-answer content such as Stack Overflow posts | | TICKETS | Work item or issue trackers such as Asana tasks, Jira tickets, or Github issues | | CODE\_REPOSITORY | Source code repositories such as Github repositories | | CHANGE\_MANAGEMENT | Code change management systems such as pull or merge requests on Github | | EMAIL | Email content such as Gmail or Outlook messages | | MESSAGING | Chat message or conversational content such as Slack or Discord messages | | CRM | Customer relationship management systems such as Sales Cloud | | SSO | Single-sign-on services such as Azure SSO or GSuite SSO | | ATS | Applicant tracking systems such as Greenhouse | | PEOPLE | This should not be used, please instead use the /bulkindexemployees API to upload data about employees | | ENTITY | Custom entities — structured, key-value objects rather than text documents (such as knowledge cards or project profiles). Used with entity datasources (`isEntityDatasource` set to `true`). | | CALENDAR | Calendar datasources. Please contact us for guidance before using this category. | | AGENTS | Agent datasources. Please contact us for guidance before using this category. | | EXTERNAL\_SHORTCUT | This should not be used; please contact us for guidance | | UNCATEGORIZED | This should not be used; please contact us for guidance | --- # Custom Properties In this document, we discuss how to define custom properties for a datasource and how to upload them with each document. tip Custom Properties are tied to a custom datasource and require re-uploading the full document on every change. If you want to enrich documents from **any** datasource (including native connectors like Google Drive, Confluence, or Jira) without re-indexing, see the [Custom Metadata API](/api-info/indexing/custom-metadata/overview.md). For a side-by-side comparison, see [Custom Properties vs Custom Metadata](/api-info/indexing/custom-metadata/custom-properties-vs-custom-metadata.md). ## What are custom properties?[​](#what-are-custom-properties "Direct link to What are custom properties?") Fundamentally, a custom property is any additional information that is attached to a document. These properties can be used in 3 different ways: * [**Operators & Facets:**](#operators-and-facets) Ability to filter documents for your search * [**Rendering:**](#rendering) Show useful information in the rendering of the search result * [**Searchability and Ranking:**](#searchability-and-ranking) Make the document more searchable and serve as a ranking signal The above 3 use-cases are explained in respective sections below. But first, we explain how you can set up and upload custom properties for your datasource. info By default, no further action is needed after configuring and uploading custom properties to use them as facets/operators and to use them for improving searchability. Rendering requires a few additional steps (described below). ## Setting up custom properties[​](#setting-up-custom-properties "Direct link to Setting up custom properties") warning It is highly recommended to configure custom properties first and then upload documents with the custom properties. Expect delays in indexing if it is done the other way round! Before custom properties can be uploaded for documents, we must configure the datasource to accept the specific custom properties. You need to define them under an object definition. The configuration can be done in 2 ways: 1. [Admin console](https://app.glean.com/admin/setup/apps/) (*recommended*) 2. API calls to [`/adddatasource`](/api/indexing-api/add-or-update-datasource.md) ### Using admin console[​](#using-admin-console "Direct link to Using admin console") **1. From the app setup page in the admin console, go to object definitions.** ![custom-properties-setup-page](/assets/images/custom-properties-setup-page-70b17a7d9ae8e6e9200fd02d130952d7.png) **2. Define your object (if you haven't already) and click on property definitions.** An object is any logical categorization of documents within your datasource (for example, documents/spreadsheets/slides can be objects for a drive datasource). ![custom-properties-object-definition](/assets/images/custom-properties-object-definition-85bf2df3bdafcfe0523c027d01f81235.png) **3. Configure the properties in the property definition modal.** ![custom-properties-property-definitions](/assets/images/custom-properties-property-definitions-cfcdc2f6e67382244c6b355230b13116.png) ### Using API Calls[​](#using-api-calls "Direct link to Using API Calls") Here is a sample API call for the above example: * curl * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/adddatasource \ -H 'Authorization : Bearer ' \ -H 'Content-Type : application/json' \ -d ' { "name": "testDatasource", "objectDefinitions": [ { "name": "Account", "displayLabel": "Account", "propertyDefinitions": [ { "name": "priority", "displayLabel": "Priority", "displayLabelPlural": "Priorities", "propertyType": "TEXT", "hideUiFacet": false } ] } ] }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: client.indexing.datasources.add( name="testDatasource", object_definitions=[ { "name": "Account", "display_label": "Account", "property_definitions": [ { "name": "priority", "display_label": "Priority", "display_label_plural": "Priorities", "property_type": "TEXT", "hide_ui_facet": False, } ], } ], ) except Exception as e: print(f"Exception when adding datasource: {e}") ``` The above API calls define a document type `Account` for our datasource `testDatasource`. For `Account` we define an operator called `priority`. This operator will become a facet if we set `hideUiFacet` to `false`. Note: * propertyType `"INT"` is not faceted. * propertyType `"DATE"` is only faceted if specified value is an epoch timestamp in seconds. ## Uploading custom properties[​](#uploading-custom-properties "Direct link to Uploading custom properties") After you have defined the properties, you can set the properties values while uploading the document. A sample API call for [`/indexdocument`](/api/indexing-api/index-document.md) where we illustrate how this is done is: Here is a sample API call for the above example: * curl * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexdocument \ -H 'Authorization : Bearer ' \ -H 'Content-Type : application/json' \ -d ' { "document": { "title": "Account 1", "id": "Account_1", "customProperties": [ { "name": "priority", "value": "High" } ] } }' ``` ``` from glean.api_client import Glean, models import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: # DocumentDefinition has many fields, we show the usage of a few basic ones. client.indexing.documents.add_or_update( document=models.DocumentDefinition( datasource="testDatasource", title="Account 1", id="Account_1", custom_properties=[ {"name": "priority", "value": "High"}, ], ) ) except Exception as e: print(f"Exception when indexing document: {e}") ``` In the above API call we set the value for the custom property `Priority` as `High` for the document Account\_1. ## Operators and Facets[​](#operators-and-facets "Direct link to Operators and Facets") ### What are operators?[​](#what-are-operators "Direct link to What are operators?") ![operators](/assets/images/operators-52d4e4dea78fb304b5806aa9906e84e5.png "#width=calc(60% - 10px);vertical-align=top;") Operators are a powerful way to filter information in Glean. In the above example we use the operators `app:` and `owner:` with query `Glean`. This tells Glean to only look for documents which match `“Glean”` and belong to `app:drive` and have `owner:”Steve Smith”`. This allows you to find the relevant information faster. ### What are Facets?[​](#what-are-facets "Direct link to What are Facets?") ![facets](/assets/images/facets-b59ecadaf4c6b71ae2cf9664e38f87a8.png "#width=calc(40% - 10px);vertical-align=top;") Facets are special kinds of operators which are shown as visual filters in the Glean UI. You can find them on the right hand side of the search result page. This removes the burden of remembering the operators and gives you a nice and interactive way of filtering results. info Glean also has a default faceting behavior. Facets are automatically created of the same name for the following document fields if they are set for a document: 1. author 2. owner 3. tags 4. updatedAt 5. objectType ### Example[​](#example "Direct link to Example") Once you follow the steps above in defining custom properties and uploading them for a document, you should be able to use the `priority` operator and see it as a facet! info If you wish to hide the facet from showing up in the UI, you may select the 'Hide UI Facet' option in the admin console for the property (or) set `hideUiFacet` to true in the API call. ## Rendering[​](#rendering "Direct link to Rendering") Apart from the above mentioned steps for configuring and uploading custom properties, if you would like to show custom properties in the search result rendering - the following 2 steps are required: 1. In the `UI options` for the custom property, select Search Result (screenshot below) **(or)** set `uiOptions` for the custom property in the API call to `SEARCH_RESULT`. ![custom-properties-ui-option](/assets/images/custom-properties-ui-option-2d716addce0c10505801fc081dcae390.png "#width=calc(60% - 10px);vertical-align=top;") 2. Contact Glean support to show the custom property to the search result as per your preference. An example is shown below. ![rendering-custom-result](/assets/images/rendering-custom-result-7a7bdb8eee53633edf53004f3b2f57ee.png) **For more details on rendering of search result refer [here](/api-info/indexing/datasource/rendering-search-results.md)** warning If you have already uploaded documents with a custom property, before setting the UI options for the property to include the Search result - please expect a delay (could be multiple hours, depending on the volume of documents) before the documents' properties are visible in the search results. To propagate this update quicker - it is recommended to re-upload the documents (or) call the [`/processalldocuments`](/api/indexing-api/schedules-the-processing-of-uploaded-documents.md) endpoint for the datasource. ## Searchability and Ranking[​](#searchability-and-ranking "Direct link to Searchability and Ranking") All custom properties are automatically used to enrich the document in the index - thereby improving searchability and ranking. For example, if we have a ticketing datasource and include a ticket ID as a custom property - we can then search by the ticket ID to fetch the relevant ticket (without requiring it to be in the title or the body!). ### Special property: `country`[​](#special-property-country "Direct link to special-property-country") In addition to general ranking improvements, a custom property named `country` has special behavior. info If you set a custom property with `name: "country"`, its value is used as a ranking signal to personalize results based on the searcher's location. * Acceptable values: ISO 3166-1 two-letter (alpha-2) or three-letter (alpha-3) country codes (for example, `US` or `USA`). Country names are also accepted, but ISO codes are preferred. * Usage: add `{"name": "country", "value": ""}` to a document's `customProperties`. --- # Rendering Search Results The rendering behavior of the search results from a datasource are determined by 3 ways (in increasing order of preference): 1. Default rendering behavior, based on the datasourceCategory (no further action required) 2. Customized render config based on requirements (please contact glean support to set this up) 3. Re-using the rendering configuration of another datasource (eg. gdrive) ## Default rendering behavior based on datasource category[​](#default-rendering-behavior-based-on-datasource-category "Direct link to Default rendering behavior based on datasource category") Datasource categories are set for a datasource during the data source setup phase (or `/adddatasource` endpoint). The [field](/api-info/indexing/datasource/category.md) is typically set for all custom data sources. For information on the meaning of each category, refer to this document: [Selecting datasource category](/api-info/indexing/datasource/category.md) With no further action required from the developer, we will aim to render results from the datasource of the specified datasource categories with information typically surfaced in results from that category. We have specific default rendering behaviour for the following datasource categories: * `PUBLISHED_CONTENT` * `KNOWLEDGE_HUB` For other categories, the rendering would be a vanilla icon + title + snippet (see screenshot below). * The [icon URL](/api/indexing-api/add-or-update-datasource.md#request) is fetched from the datasource config (as defined during the /adddatasource call (or) in the datasource setup page in the admin console). * The snippets are generated based on the relevance between the query and the provided body text. * The title is provided in the [display name](/api/indexing-api/add-or-update-datasource.md#request) while indexing documents. ![rendering-simple-result](/assets/images/rendering-simple-result-c95340c5bd7b79b49b63ec31a1e8f480.png "#width=calc(90% - 10px);vertical-align=top;") Contact glean support to further customize rendering for your datasource (option 2). ### PUBLISHED\_CONTENT[​](#published_content "Direct link to PUBLISHED_CONTENT") Results from datasources with the datasourceCategory set to `PUBLISHED_CONTENT` would include the [last updated time](/api/indexing-api/bulk-index-documents.md#request), [author](/api/indexing-api/bulk-index-documents.md#request) and [container](/api/indexing-api/bulk-index-documents.md#request) (if any) in the meta line. ### KNOWLEDGE\_HUB[​](#knowledge_hub "Direct link to KNOWLEDGE_HUB") Similar to `PUBLISHED_CONTENT`, datasources with `KNOWLEDGE_HUB` would include the [last updated time](/api/indexing-api/bulk-index-documents.md#request), [owner](/api/indexing-api/bulk-index-documents.md#request) (instead of author) and [container](/api/indexing-api/bulk-index-documents.md#request) (if any) in the meta line. Datasources with the above 2 categories, typically look like the following: ![rendering-sample-result](/assets/images/rendering-sample-result-e0cafbf06517ba0f6024d2f491067593.png "#width=calc(90% - 10px);vertical-align=top;") ## Customized render config based on requirements[​](#customized-render-config-based-on-requirements "Direct link to Customized render config based on requirements") info Currently, this step requires contacting glean support to get the behaviour support. Examples of requests include: * I need a custom list of attributes to show up in the ‘meta line’ (ie. the line below the title) * I need a different icon to show up for different object types For now, this will be setup by Glean internally - but in the future we expect to enable admins/developers to configure this themselves via the Glean admin console. Here is an example of customized rendering for a search result: ![rendering-custom-result](/assets/images/rendering-custom-result-7a7bdb8eee53633edf53004f3b2f57ee.png "#width=calc(90% - 10px);vertical-align=top;") The example above is for an internal job portal datasource and shows how we can add custom attributes like level, location, job role, engineering manager (as a person field). ## Re-using rendering configuration of another datasource[​](#re-using-rendering-configuration-of-another-datasource "Direct link to Re-using rendering configuration of another datasource") To do this, you can set the [renderConfigPreset](/api/indexing-api/add-or-update-datasource.md#request) in the datasource config (when making /adddatasource calls or setting up a datasource via the admin console). This field can be set to the name of any datasource supported by glean (in lower case). While it is recommended to contact Glean support to help get this setup - some of the common values used for this field include: * gdrive * jira * zendesk * salesforce info If you want to go back to using the default rendering behaviour (option 1) OR the custom rendering setup through option 2, you **must unset the renderConfigPreset in the datasource config.** --- # Test Datasource Before going live with a production datasource you might want to set up a test/staging datasource. You can use a test datasource to experiment with the indexing API, or to stage your changes. This test datasource lives in the production environment but doesn’t affect it. You can do this by ensuring that: 1. All ranking signals are turned off from a test datasource. This ensures that the production is not affected by a test datasource. 2. The test datasource is only visible to select users so that a developer can check results and permissions easily. Setting up a test datasource is quite easy, you have to go through almost the same steps as you would go through setting up a normal datasource, but there are some differences. The steps are: ### 1. Setting `isTestDatasource` to true[​](#1-setting-istestdatasource-to-true "Direct link to 1-setting-istestdatasource-to-true") When setting up the datasource using [`/adddatasource`](/api/indexing-api/add-or-update-datasource.md), remember to set the isTestDatasource field to true. * curl * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/adddatasource \ ... "isTestDatasource": true ... ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: client.indexing.datasources.add( name="gleantest", # ... other fields is_test_datasource=True, ) except Exception as e: print(f"Exception when adding datasource: {e}") ``` Glean turns off ranking signals from a test datasource. For example, Glean uses document references(link to a document) as one of its ranking signals. A document that is referenced more in other documents will be given more importance then a less referenced one. Glean stops all such signals for a test datasource so that it does not pollute production rankings. ### 2. Using `/betausers` endpoint to give select visibilty to users.[​](#2-using-betausers-endpoint-to-give-select-visibilty-to-users "Direct link to 2-using-betausers-endpoint-to-give-select-visibilty-to-users") At this stage your test datasource is not affecting the production and it is neither visible to anyone. Now you would like to give selective visibility to it. This can be done using the [`/betausers`](/api/indexing-api/beta-users.md) endpoint. This endpoint allows you to control the people who can have visibility to the test datasource. An example command would look like: * curl * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/betausers \ -H 'Authorization: Bearer ' \ -H 'Content-Type : application/json' \ -d ' { "datasource": "gleantest", "emails": [ "user1@example.com", "user2@example.com" ] }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: client.indexing.permissions.authorize_beta_users( datasource="gleantest", emails=[ "user1@example.com", "user2@example.com", ], ) except Exception as e: print(f"Exception when authorizing beta users: {e}") ``` This command would give visibility of the test datasource to user1 and user2. This would allow you to test your custom datasource among a small number of users. Note: For a document to be visible to beta users, they must also have appropriate permissions to view it. For more details about setting permissions, follow the [Setting Permissions](/api-info/indexing/documents/permissions.md) tutorial. --- # Datasource Config Gather information about the datasource's config. ### Sample Request[​](#sample-request "Direct link to Sample Request") * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/getdatasourceconfig \ -H 'Authorization: Bearer ' \ -H 'Content-Type : application/json' \ -d '{ "datasource": "gleantest" }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: res = client.indexing.datasources.retrieve_config(datasource="gleantest") print(res) except Exception as e: print(f"Exception when getting datasource config: {e}") ``` ### Sample Response[​](#sample-response "Direct link to Sample Response") ``` { "name": "CUSTOM_GLEANTEST", "displayName": "Glean Test Datasource", "homeUrl": "https://www.gleantest.com", "objectDefinitions": [ { "name": "EngineeringDoc", "propertyDefinitions": [ { "name": "Org", "displayLabel": "Organization", "propertyType": "TEXT", "uiOptions": "SEARCH_RESULT", "hideUiFacet": false } ] } ], "urlRegex": "https://www.gleantest.com/.*", "datasourceCategory": "PUBLISHED_CONTENT", "isOnPrem": false, "isUserReferencedByEmail": true, "isEntityDatasource": false, "isTestDatasource": false } ``` * `/getdatasourceconfig` endpoint returns the current state of the datasource config. * This can be used to double-check if the config is correct. If you need to make any changes to the config, you can use the `/adddatasource` endpoint again to override the previous config. --- # Datasource Document Gives various information that would help in debugging issues related to a particular document. For example, it gives information about the document's upload and indexing status, the document's permissions, etc. ### Sample Request[​](#sample-request "Direct link to Sample Request") * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/debug/{datasource}/document -H 'Authorization : Bearer ' \ -H 'Content-Type : application/json' \ -d '{ "objectType": "Article", "docId": "art123" }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: res = client.indexing.documents.debug( datasource="gleantest", object_type="Article", doc_id="art123", ) print(res) except Exception as e: print(f"Exception when debugging document: {e}") ``` ### Sample Response[​](#sample-response "Direct link to Sample Response") ``` { "status": { "uploadStatus": "UPLOADED", "lastUploadedAt": "2024-02-08T12:00:00.000Z", "indexingStatus": "INDEXED", "lastIndexedAt": "2024-02-08T12:05:00.000Z", "permissionIdentityStatus": "UPLOADED" }, "uploadedPermissions": { "allowAnonymousAccess": false, "allowedUsers": [ { "email": "user1@example.com" }, { "email": "user2@example.com" } ], "allowedGroups": [ "group1" ] } } ``` --- # Datasource Status Gather information about the datasource's overall status. ### Sample Request[​](#sample-request "Direct link to Sample Request") * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/debug/gleantest/status \ -H 'Authorization: Bearer ' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: # Get datasource status try: res = client.indexing.datasource.status(datasource="gleantest") print(res) except Exception as e: print(f"Exception when getting datasource status: {e}") ``` ### Sample Response[​](#sample-response "Direct link to Sample Response") ``` { "documents": { "bulkUploadHistory": [ { "uploadId": "upload-id-1234567890", "startTime": "2024-02-08T12:00:00.000Z", "endTime": "2024-02-08T12:05:00.000Z", "status": "SUCCESSFUL" } ], "counts": { "uploaded": [ { "objectType": "Article", "count": 15 } ], "indexed": [ { "objectType": "Article", "count": 15 } ] }, "processingHistory": [ { "startTime": "2024-02-08T12:00:00.000Z", "endTime": "2024-02-08T12:00:05.000Z" } ] }, "identity": { "processingHistory": [ { "startTime": "2024-02-08T12:00:00.000Z", "endTime": "2024-02-08T12:05:00.000Z" } ], "users": { "bulkUploadHistory": [ { "uploadId": "upload-users-1234567890", "startTime": "2024-02-08T12:00:00.000Z", "endTime": "2024-02-08T12:05:00.000Z", "status": "SUCCESSFUL" } ], "counts": { "uploaded": 5 } }, "groups": { "bulkUploadHistory": [], "counts": { "uploaded": 3 } }, "memberships": { "bulkUploadHistory": [], "counts": { "uploaded": 2 } } } } ``` --- # Datasource User Gives various information that would help in debugging issues related to a particular user. For example, it gives information about the user's permissions, groups, memberships, etc. ### Sample Request[​](#sample-request "Direct link to Sample Request") * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/debug/gleantest/user -H 'Authorization : Bearer ' \ -H 'Content-Type : application/json' \ -d '{ "email": "user1@example.com" }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: res = client.indexing.people.debug( datasource="gleantest", email="user1@example.com", ) print(res) except Exception as e: print(f"Exception when debugging user: {e}") ``` ### Sample Response[​](#sample-response "Direct link to Sample Response") ``` { "status": { "isActiveUser": true, "uploadStatus": "UPLOADED", "lastUploadedAt": "2024-02-08T12:00:00.000Z" }, "uploadedGroups": [ { "name": "Engineering" } ] } ``` --- # Document Access Check if a user has access to a document. ### Sample Request[​](#sample-request "Direct link to Sample Request") * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/checkdocumentaccess -H 'Authorization : Bearer ' \ -H 'Content-Type : application/json' \ -d '{ "datasource": "gleantest", "objectType": "Article", "docId": "art123", "userEmail": "user1@example.com" }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: res = client.indexing.documents.check_access( datasource="gleantest", object_type="Article", doc_id="art123", user_email="user1@example.com", ) print(res) except Exception as e: print(f"Exception when checking document access: {e}") ``` ### Sample Response[​](#sample-response "Direct link to Sample Response") ``` { "hasAccess": true } ``` --- # Document Count Get the count of documents in a datasource. Deprecated The `/getdocumentcount` endpoint is deprecated (deprecated on 2026-02-03, with removal scheduled for 2026-10-15). Use [Datasource Status](/api-info/indexing/debugging/datasource-status.md) (`/debug/{datasource}/status`) instead, which returns richer document counts and status information. ### Sample Request[​](#sample-request "Direct link to Sample Request") * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/getdocumentcount -H 'Authorization : Bearer ' -H 'Content-Type : application/json' \ -d '{ "datasource": "gleantest" }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: try: res = client.indexing.documents.count(datasource="gleantest") print(res) except Exception as e: print(f"Exception when getting document count: {e}") ``` ### Sample Response[​](#sample-response "Direct link to Sample Response") ``` { "documentCount": 15 } ``` --- # Activity We allow the option of allowing developers to push activity information for documents (VIEW, EDIT etc.) - that will be used for better ranking of documents for search. ## Getting started with the activity endpoint[​](#getting-started-with-the-activity-endpoint "Direct link to Getting started with the activity endpoint") First note that, the [activity endpoint](/api/client-api/activity/activity.md) needs to be called with a **client API** bearer token (as opposed to an indexing API bearer token for other endpoints). This is because, we expect activity to be provided server-to-server on a per-user basis (eg. when a user views a document from a custom datasource, you may report this view back to us from your server). For pushing activity for any user from your organization, we need a *Global* client API token scoped to *ACTIVITY*. To fetch a scoped client API token, please follow the instructions in the [Authentication page for Client API](/api-info/client/authentication/glean-issued.md#available-scopes). ### Making your first request[​](#making-your-first-request "Direct link to Making your first request") Below is a sample activity request for a given user (who needs to be specified in the **X-Glean-ActAs** param). We record 2 view events: One for and another for . We specify the datasource in the params field (so it is correctly matched to the relevant document) and optionally, we also mention the duration of the view if available. ``` curl -i -X POST \ 'https://customer-be.glean.com/rest/api/v1/activity' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -H 'X-Glean-ActAs: user-whose-activity-is-being-reported@example.com' \ -d '{ "events": [ { "url": "https://example.com/doc1", "action": "VIEW", "timestamp": "2023-04-05T04:56:07.000Z", "params": { "datasource": "testdatasource" } }, { "url": "https://example.com/doc2", "action": "VIEW", "timestamp": "2023-04-04T04:56:07.000Z", "params": { "duration": 20, "referrer": "https://example.com/document", "datasource": "testdatasource2" } } ] }' ``` --- # Bulk Indexing Bulk index documents endpoint is used to index all the documents of a custom datasource using a series of `/bulkindexdocuments` requests with a common `uploadId`. Bulk indexing fully replaces the entire list of documents stored in Glean. After a successful bulk upload, all documents that were not a part of the most recent upload are deleted asynchronously. There are similar bulk indexing endpoints for other objects like users, groups, employees, and teams as well. ## Choosing `/indexdocuments` vs `/bulkindexdocuments`[​](#choosing-indexdocuments-vs-bulkindexdocuments "Direct link to choosing-indexdocuments-vs-bulkindexdocuments") When deciding between `/indexdocuments` and `/bulkindexdocuments`, it's important to understand their primary functions and use cases: * `/bulkindexdocuments`: This endpoint is designed for completely refreshing the datasource. It deletes all existing documents and replaces them with the new ones provided. Use this endpoint when you need to replace the existing corpus and upload all documents anew. * `/indexdocuments`: This endpoint is intended for incremental updates. It allows you to add a batch of new documents or update existing ones without affecting the other documents in the index. Choose this option when you want to keep the existing documents intact while adding or updating specific documents. When to use each endpoint: * Use `/bulkindexdocuments`: * When you need to perform a full refresh of the datasource. * When all existing documents need to be replaced with a new set of documents. * Use `/indexdocuments`: * When you need to add new documents to the existing index. * When you need to update specific documents while keeping the rest of the index unchanged. By selecting the appropriate endpoint based on your needs, you can efficiently manage your document indexing process. ## Making your first successful request to `/bulkindexdocuments`[​](#making-your-first-successful-request-to-bulkindexdocuments "Direct link to making-your-first-successful-request-to-bulkindexdocuments") Here is a sample request to the `/bulkindexdocuments` endpoint. * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/bulkindexdocuments \ -H 'Authorization: Bearer ' \ -H 'Content-Type : application/json' \ -d ' { "uploadId": "test-upload-id", "isFirstPage": true, "isLastPage": true, "forceRestartUpload": true, "datasource": "gleantest", "documents": [ { "datasource": "gleantest", "objectType": "EngineeringDoc", "id": "test-doc-1", "title": "How to bulk index documents", "body": { "mimeType": "text/plain", "textContent": "This doc will help you make your first successful bulk index document request" }, "permissions": { "allowedUsers": [ { "email": "myuser@bluesky.test", "datasourceUserId": "myuser-datasource-id", "name": "My User" } ], "allowAllDatasourceUsersAccess": true }, "viewURL": "https://www.glean.engineering.co.in/test-doc-1", "customProperties": [ { "name": "Org", "value": "Infrastructure" } ] } ] }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: # Bulk index documents try: client.indexing.documents.bulk_index(request={ "uploadId": "test-upload-id", "datasource": "gleantest", "documents": [{ "datasource": "gleantest", "objectType": "EngineeringDoc", "id": "test-doc-1", "title": "How to bulk index documents", "body": { "mimeType": "text/plain", "textContent": "This doc will help you make your first successful bulk index document request" }, "permissions": { "allowAnonymousAccess": True }, "viewURL": "https://www.glean.engineering.co.in/test-doc-1", "customProperties": [ { "name": "Org", "value": "Infrastructure" } ] }], "isFirstPage": True, "isLastPage": True, "forceRestartUpload": True }) except Exception as e: print(f"Exception when bulk indexing documents: {e}") ``` Let's look at the different fields you need to successfully index documents to Glean. Note that this is just a sample request with minimal fields required to index content. For exhaustive list of fields, please refer [here](/api/indexing-api/bulk-index-documents.md). ## Next steps[​](#next-steps "Direct link to Next steps") * You can check the status of your document using our debugging/troubleshooting APIs. Please refer [here](/api/indexing-api/troubleshooting-overview.md) for documentation on how to use these APIs. * For the indexed document to show up in Glean UI, the datasource must be enabled for search. Glean admins can enable it from the 'Overview' tab on the setup page for the custom app in the [admin console](https://app.glean.com/admin/setup/apps/), where visibility can be configured for all teammates or a configured test group. Once these steps are done, you should be able to search for the indexed document in Glean when logged in as the user having permissions to view the documents. * Note that it takes around 15-20 minutes for the documents to be indexed and appear on your Glean UI. --- # Bulk Upload Model The bulk upload endpoints delete all entries that are not a part of the most recent upload. For example, `/bulkindexdocuments` endpoint would delete all the documents that are not present in the most recent upload. Concurrent uploads are not allowed ie. you cannot start a new upload before the previous upload is finished. There are some fields that are common across all bulk upload endpoints. We will be describing them here: | Field Name | Description | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `uploadId` | This is the id which uniquely identifies an upload. You need to have a unique uploadId for all the paginated requests you send for an upload. | | `isFirstPage` | This denotes whether the page being uploaded is the first page, and needs to be true for the first request and false for all subsequent requests for an upload. | | `isLastPage` | This denotes whether the page being uploaded is the last page, and needs to be true for the last request and false for all other requests for an upload. | | `forceRestartUpload` | This is required if you want to start a new upload but the previous upload has not finished or has failed. Not specifying this bit in case of an unsuccessful previous upload will fail the request. | | `disableStaleDocumentDeletionCheck` | The `/bulkindexdocuments` asynchronously deletes all documents that weren’t a part of the most recent upload session. This can lead to accidental situations where too many documents get wiped in case of an erroneous bulk upload.

To mitigate this, we have a deletion check in place which pauses the deletion of stale documents for 7 days if the percentage of docs being deleted exceeds 20%.

In case you intentionally want to delete more than 20% of your previously uploaded documents, you can specify `disableStaleDocumentDeletionCheck = true`, which disables this check and allows the documents to be deleted. Note that documensts are delete asynchronously.

If you wish for deletions to take effect immediately, use `/processalldocuments` endpoint. | --- # Document Model The following is the basic document model used for indexing a new document to Glean. There are other fields too which you can use for advanced functionality. You can refer to them in the API reference docs [here](https://developers.glean.com/api/indexing-api/index-document). | Field | Description | | ------------------------------- | -------------------------------------------------------------------------------------------------------- | | `datasource` | Represents the document datasource | | `id` | Unique identifier for the document | | `body` | Content which will be used to populate the document body and its mime type | | `allowedUsers` | List of users who will be able to view this document | | `viewURL` | Document view url | | `allowAnonymousAccess` | If set to true, anyone who is signed into Glean can view search results for the document | | `allowAllDatasourceUsersAccess` | If set to true, all users of the datasource (as uploaded using the Identity APIs) can view this document | | `customProperties` | List of name - value pairs used to populate additional facets | --- # Permissions The content in Glean is highly customizable in terms of configuring visibility of documents, which can be noted while [indexing documents](/api/indexing-api/index-document.md#request) using Indexing API endpoints. Glean may require some more information to manage users, groups and group memberships to enable the full power of customized permissions. This can be done using [Permissions API endpoints](/api/indexing-api/permissions-overview.md). This tutorial assumes that you have followed the documentation / other tutorials to [set up a custom datasource](/api/indexing-api/add-or-update-datasource.md) and know how to use the Indexing API to [index documents](/api-info/indexing/getting-started/index-documents.md). If not, check out the documentation and other tutorials available. We shall consider various scenarios with increasing complexity of permissions used, along with some example requests which can be used to reproduce them. ## Scenario 0: Allowing any glean user to search for a document[​](#scenario-0-allowing-any-glean-user-to-search-for-a-document "Direct link to Scenario 0: Allowing any glean user to search for a document") *** The most basic way to configure permissions for a document is to allow **any** Glean user within the organization to be able to search for a document. This can be accomplished by setting the [`allowAnonymousAccess`](/api/indexing-api/index-document.md#request) field to be **true** while setting up permissions for a document. For example, the following request to index a document would lead to the document being visible to all Glean users: ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexdocument \ #... "permissions": { "allowAnonymousAccess": "true" } #... Rest of the document ``` ## Scenario 1: Explicitly allowing users that can search for a new document[​](#scenario-1-explicitly-allowing-users-that-can-search-for-a-new-document "Direct link to Scenario 1: Explicitly allowing users that can search for a new document") *** Let’s address a simple scenario: We have a document and want to explicitly allow some users to be able to search for it using Glean. There are two steps required to achieve the above use-case: 1. Index users into Glean: When using non-anonymous permissions, users need to be indexed before being referenced. This is an important step to let Glean know about the users that use a particular datasource before being referenced elsewhere (for example: while defining group memberships, document permissions). 2. Add user-specific permissions to the document: While indexing a documemt, set the corresponding [`allowedUsers`](/api/indexing-api/index-document.md#request) field to capture users that can search for it. ### Step 1.1: Index users into Glean[​](#step-11-index-users-into-glean "Direct link to Step 1.1: Index users into Glean") *** When using non-anonymous permissions, users need to be indexed before being referenced. This is an important step to let Glean know about the users that may have access to the datasource, before they are referenced while defining group memberships, document permissions, etc. This can be done using the [`indexuser`](/api/indexing-api/index-user.md) or the [`/bulkindexusers`](/api/indexing-api/bulk-index-users.md) endpoint. Note that this does not give permissions to the users to search for the document, but only let's Glean know that these users exist and can be referenced later. Let us look at the following sample request to understand better. The following request indexes a user with email `user1@test.com` and userId `gleantest_user_1`, into the datasource `gleantest`: ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexuser \ -H 'Authorization: Bearer ' \ -d ' { "datasource": "gleantest", "user": { "email": "user1@test.com", "userId": "gleantest_user_1" # This can be ignored if not applicable "name": "Test User 1", "isActive": "true" } }' ``` ### Step 1.2: Adding user-specific permissions to the document[​](#step-12-adding-user-specific-permissions-to-the-document "Direct link to Step 1.2: Adding user-specific permissions to the document") *** Once the users are indexed, we can create a document with its [`allowedUsers`](/api/indexing-api/index-document.md#request) field set appropriately to capture users that can search for it. Based on the [`isUserReferencedByEmail`](/api/indexing-api/add-or-update-datasource.md#request) field set to **true** or **false** in the datasource config, we can follow one of the two scenarious below: #### Scenario 1.2-A: [`isUserReferencedByEmail`](/api/indexing-api/add-or-update-datasource.md#request) field set to **true** in the datasource config[​](#scenario-1a "Direct link to scenario-1a") *** In case [`isUserReferencedByEmail`](/api/indexing-api/add-or-update-datasource.md#request) is set to **true**, users should be referenced by their email when adding permissions to a document. This can be done by setting [`email`](/api/indexing-api/index-document.md#request) under the [`allowedUsers`](/api/indexing-api/index-document.md#request) field for each user that we want to give permissions to. The following sample creates a document, giving permissions to only `user1@test.com` (**assuming a user with this email has been indexed**) to search for this document: ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexdocument \ # ... "permissions": { "allowedUsers": [ { "email": "user1@test.com", } ] } # ... Rest of the document ``` #### Scenario 1.2-B: [`isUserReferencedByEmail`](/api/indexing-api/add-or-update-datasource.md#request) field set to **false** in the datasource config[​](#scenario-1b "Direct link to scenario-1b") *** In case [`isUserReferencedByEmail`](/api/indexing-api/add-or-update-datasource.md#request) is set to **false**, users should be referenced by their datasourceUserId when adding permissions to a document. This can be done by setting [`datasourceUserId`](/api/indexing-api/index-document.md#request) under the [`allowedUsers`](/api/indexing-api/index-document.md#request) field for each user that we want to give permissions to. The following sample creates a document, giving permissions to only `gleantest_user_1` (**assuming a user with this userId has been indexed**) to search for this document: ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexdocument \ # ... "permissions": { "allowedUsers": [ { "datasourceUserId": "gleantest_user_1", } ] } # ... Rest of the document ``` ## Scenario 2: Allowing all users of the datasource to search a document[​](#scenario-2 "Direct link to Scenario 2: Allowing all users of the datasource to search a document") *** We shall move to a different scenario: We now want to allow all users of the datasource to be able to access a particular document. To achieve this, we must first define “all users of the datasource” by indexing all relevant users into Glean. For example, if we only want `user1@test.com` to be a “datasource user”. We use the following sample request to index the user. ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexuser \ -H 'Authorization: Bearer ' \ -d ' { "datasource": "gleantest", "user": { "email": "user1@test.com", "userId": "gleantest_user_1" # This can be ignored if not applicable "name": "Test User 1", "isActive": "true" } }' ``` Next, we should set the [`allowAllDatasourceUsersAccess`](/api/indexing-api/index-document.md#request) field set to **true** to allow all datasource users to be able to search for the particular document. The following sample request creates a document that is accessible to all datasource users (i.e. only `user1@test.com` in this case) using Glean. ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexdocument \ # ... "permissions": { "allowAllDatasourceUsersAccess": "true" } # ... Rest of the document ``` ## Scenario 3: Allowing pre-defined groups of users to search for a document[​](#scenario-3-allowing-pre-defined-groups-of-users-to-search-for-a-document "Direct link to Scenario 3: Allowing pre-defined groups of users to search for a document") *** For this scenario, we want to pre-define specific **groups** of users which are all to be given access to a document. For instance, a specific team within the organization may have access to a chunk of documents within the datasource. This can be achieved by indexing a group with all members of that team and allowing this group to access each relevant document. We shall learn more about this using an example. Let us assume that we have two users `user1@test.com` and `user2@test.com` and the datasource is configured to have the [`isUserReferencedByEmail`](/api/indexing-api/add-or-update-datasource.md#request) field set to **true**. We shall index a group `gleantest_group_1` with the two users as their members, and then create a document that allows `gleantest_group_1` to access it. This should implicitly allow `user1@test.com` and `user2@test.com` to search for the document using Glean. The following sequence of steps (with sample API calls) should help us reproduce the above mentioned situation: 1. Index users into Glean: Index users `user1@test.com` and `user2@test.com` before moving to further steps (To learn how to index users, please refer to [Step 1.1](/api-info/indexing/documents/permissions.md#step-11-index-users-into-glean) above).
2. Index a group named `gleantest_group_1` using the [`/indexgroup`](/api/indexing-api/index-group.md) endpoint: ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexgroup \ -H 'Authorization: Bearer ' \ -d ' { "datasource": "gleantest", "group": { "name":"gleantest_group_1" } }' ``` Some key points about fields in the request body: * [`name`](/api/indexing-api/index-group.md#request): It is the identifying key for a group within a datasource. There are a few considerations while defining the name of the group: * Cannot contain any whitespaces * Cannot be empty * Cannot start with the prefix “scio”
3. Index memberships for each of the two test users using the [`/indexmembership`](/api/indexing-api/index-membership.md) endpoint: ``` # Index membership for user1@test.com curl -X POST https://customer-be.glean.com/api/index/v1/indexmembership \ -H 'Authorization: Bearer ' \ -d ' { "datasource": "gleantest", "membership": { "groupName": "gleantest_group_1", "memberUserId": "user1@test.com" } }' # Index membership for user2@test.com curl -X POST https://customer-be.glean.com/api/index/v1/indexmembership \ -H 'Authorization: Bearer ' \ -d ' { "datasource": "gleantest", "membership": { "groupName": "gleantest_group_1", "memberUserId": "user2@test.com" } }' ``` * Some key points about fields in the request body: * `groupName`: A group with this particular groupName must be indexed before indexing memberships for it. * `memberUserId`: This identifies a particular user as a member of the group. This must be the email ID of the user if [`isUserReferencedByEmail`](/api/indexing-api/add-or-update-datasource.md#request) is set to true. In the other case, this field must be the userId specified while indexing the user. * Note: In case a group needs to be added as a member of another group, that can be done by using the [`memberGroupName`](/api/indexing-api/index-membership.md#request) field in place of the `memberUserId` field (Exactly one of the two fields must be present).
4. Create a document with the [allowedGroups](/api/indexing-api/index-document.md#request) field to include `gleantest_group_1`. This would allow members of the group to search for the document using Glean. ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexdocument \ # ... "permissions": { "allowedGroups": ["gleantest_group_1"] } # ... Rest of the document ``` ## Notes and next steps[​](#notes-and-next-steps "Direct link to Notes and next steps") * Note: Permissions and memberships are processed asynchronously, there might be a small delay before documents are visible to groups / users in Glean searches. * This tutorial only covers singleton variants of endpoints that have a bulk upload version. The bulk upload endpoints are similar to their singleton variants, following the [bulk upload model](/api-info/indexing/documents/bulk-upload-model.md) and can be used when bulk indexing is required. * You can check permissions by using the [`/checkdocumentaccess`](/api/indexing-api/check-document-access.md) API endpoint. Check out the [debugging/troubleshooting tutorial](/api/indexing-api/troubleshooting-overview.md) for examples and additional helpful information. --- # Supported MIME Types The Indexing API supports a variety of MIME types for various content fields in documents. This document provides a list of popular MIME types which are supported and guidelines on how to use them effectively. ## Supported[​](#supported "Direct link to Supported") The following are some popular MIME types which are supported by the Indexing API: | MIME Type | Description | | --------------------------------------------------------------------------- | --------------------------------- | | `application/vnd.apple.keynote` | Apple Keynote presentation | | `application/epub+zip` | eBook file | | `application/msword` | Microsoft Word document | | `application/onenote` | Microsoft OneNote document | | `application/pdf` | PDF document | | `application/rtf` | Rich Text Format document | | `application/vnd.apple.pages` | Apple Pages document | | `application/vnd.google-apps.form` | Google Forms document | | `application/vnd.google-apps.site` | Google Sites page | | `application/vnd.ms-excel` | Microsoft Excel document | | `application/vnd.openxmlformats-officedocument.presentationml.presentation` | Microsoft PowerPoint presentation | | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | Microsoft Excel spreadsheet | | `application/x-apple-diskimage` | Apple Disk Image | | `application/x-executable` | Executable file | | `application/x-shockwave-flash` | Flash file | | `text/csv` | Comma-separated values file | | `text/html` | HTML document | | `text/markdown` | Markdown document | | `text/plain` | Plain text document | | `text/tab-separated-values` | Tab-separated values file | | `text/vcard` | vCard file | Please note that this list is not exhaustive and we support many other MIME Types. If you have query about a particular MIME Type which is not listed, please contact Glean support. ## Unsupported[​](#unsupported "Direct link to Unsupported") The following are some popular MIME types which are not supported by the Indexing API: | MIME Type | Description | | ------------------ | -------------- | | `application/json` | JSON file | | `application/xml` | XML file | | `text/css` | CSS file | | `text/xml` | XML file | | `video` | Video file | | `application/mp4` | MP4 video file | | `image` | Image file | | `audio` | Audio file | | `zip` | ZIP archive | | `rar` | RAR archive | ## Additional Information[​](#additional-information "Direct link to Additional Information") For more details on how to use the Indexing API to start indexing a custom datasource, refer to the [Indexing API Quickstart](/api-info/indexing/getting-started/overview.md). If you encounter any issues or have questions about supported MIME types, please contact our support team. --- # Index Documents This guide will help you quickly get familiar with the basics of using the Indexing API by configuring a datasource, indexing a document, and enabling the datasource to show up in search. ## Index a document for the datasource[​](#index-a-document-for-the-datasource "Direct link to Index a document for the datasource") Once the datasource is configured, we can index documents. A document has the following key fields. `id` This is the datasource-specific identifier for the document within the datasource. It is case-insensitive and must be no more than 200 characters in length. The id should be stable, meaning that the same document must keep the same id across uploads. `id` is a required field for datasources created after March 1, 2025; for older datasources, if an id is not provided you must supply a `viewURL`, which is then used to identify the document. `objectType` Type of object within the datasource. For example, a drive might have objects of type file and folder. `title` Title of the document. `body` Searchable document body. This might be shown in search result snippets. `viewURL` A unique URL that can used to view the document in a browser. This viewURL must also match the urlRegex specified while creating the datasource. `permissions` This can be used to control visibility of the document in search results for different Glean users. For simplicity, in this tutorial, we will only index a document with anonymous access using `permissions.allowAnonymousAccess`. * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexdocument \ -H 'Authorization: Bearer ' \ -H 'Content-Type : application/json' \ -d '{ "document": { "datasource": "gleantest", "objectType": "EngineeringDoc", "id": "blueskytest-1", "title": "Getting started with Blue Sky", "body": { "mimeType": "text/plain", "textContent": "This doc will help you get familiar with Blue Sky API" }, "permissions": { "allowAnonymousAccess": true }, "viewURL": "https://bluesky.test/blueskytest-1" } }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: # Index a document try: client.indexing.documents.index(request={ "document": { "datasource": "gleantest", "objectType": "EngineeringDoc", "id": "blueskytest-1", "title": "This doc will help you get familiar with Blue Sky API", "viewURL": "https://bluesky.test/blueskytest-1", "body": { "mimeType": "text/plain", "textContent": "This doc will help you get familiar with Blue Sky API" }, "permissions": { "allowAnonymousAccess": True } } }) except Exception as e: print(f"Exception when indexing document: {e}") ``` To learn about more document customization options [here](/api/indexing-api/index-document.md) To learn more about how to set up user identities, and more complex permissions, read [Setting permissions](/api-info/indexing/documents/permissions.md) To index documents in bulk, you can use [Bulk indexing](/api-info/indexing/documents/bulk-indexing.md) For helpful troubleshooting tips, read [Troubleshooting](/api-info/indexing/debugging/datasource-config.md) ## Enable search results for the datasource[​](#enable-search-results-for-the-datasource "Direct link to Enable search results for the datasource") To be able to search for the indexed document in Glean, it must be enabled show up in search results. Glean admins can go to the 'Overview' tab on the setup page for their custom app to enable the datasource to show up in search results. Results can be configured to be visible to all teammates or to their configured test group info Document permissions are respected even when results are enabled for `All teammates` or `Test group only`. Enabling results for `All teammates` / `Test group only` does **not** mean configured permissions for documents will be overridden. ![Enable search results](/assets/images/custom_datasource_setup_enable_results-163e35f50174a48a789557dfc01f681c.png) ## Success\![​](#success "Direct link to Success!") success Once these steps are done, you should be able to search for the indexed document in Glean when logged in as the user added above. Note that it can take a few minutes for the document to reflect in the index after an /indexdocument call. ## More examples[​](#more-examples "Direct link to More examples") Take a look at our open-sourced repository for more code examples: [![Github repository badge](https://img.shields.io/badge/gleanwork-indexing_api_connectors-blue?logo=github)](https://github.com/gleanwork/indexing-api-connectors/) Refer to the [Wikipedia](https://github.com/gleanwork/indexing-api-connectors/tree/main/wikipedia) toy example which indexes some relevant articles from Wikipedia using the Indexing API. note Help us improve by contributing to the open-sourced repository by suggesting more examples and creating issues with feedback! --- # Indexing API Quickstart Create a datasource and index one document. This is the shortest end-to-end path to making custom content available to Glean; use the [Indexing API overview](/api/indexing-api.md) to explore other content types and workflows. ## Before you begin[​](#before-you-begin "Direct link to Before you begin") 1 Find your API URL If automatic discovery is unavailable, ask a Glean admin for the complete **Server instance (QE)** value. Glean admins can copy it from the [About Glean page](https://app.glean.com/admin/about-glean). 2 Create an API token Create an Indexing API token by following the [Indexing API authentication guide](/api-info/indexing/authentication/overview.md). 3 Plan your datasource Choose a stable datasource name and a URL pattern that matches the documents you plan to index. ## 1. Create a datasource[​](#1-create-a-datasource "Direct link to 1. Create a datasource") Replace the token, any placeholder API URL, and the example datasource values, then run the request: ``` curl -X POST https://customer-be.glean.com/api/index/v1/adddatasource \ -H 'Authorization: Bearer ' \ -d '{ "name": "internal-docs", "displayName": "Internal Documentation", "datasourceCategory": "PUBLISHED_CONTENT", "urlRegex": "^https://internal.company.com/docs.*", "isUserReferencedByEmail": true }' ``` For field selection, admin-console setup, and an API-client example, see [Set Up a Datasource](/api-info/indexing/getting-started/setup-datasource.md). ## 2. Index a document[​](#2-index-a-document "Direct link to 2. Index a document") Use the same datasource name in your first document request: ``` curl -X POST https://customer-be.glean.com/api/index/v1/indexdocument \ -H 'Authorization: Bearer ' \ -d '{ "document": { "datasource": "internal-docs", "objectType": "Document", "id": "getting-started-guide", "title": "Getting Started Guide", "body": { "mimeType": "text/plain", "textContent": "This guide helps new employees get up to speed quickly..." }, "permissions": { "allowAnonymousAccess": false, "allowedUsers": [{ "email": "employee@company.com" }] }, "viewURL": "https://internal.company.com/docs/getting-started" } }' ``` For document fields, permissions, bulk indexing, and API-client examples, see [Index Documents](/api-info/indexing/getting-started/index-documents.md). ## 3. Enable and verify the datasource[​](#3-enable-and-verify-the-datasource "Direct link to 3. Enable and verify the datasource") In the Glean Admin Console, open the custom datasource and enable its content for a test group or all teammates. Indexing is asynchronous, so the document can take a few minutes to appear in search. Verify it as a user allowed by the document permissions. ## Next steps[​](#next-steps "Direct link to Next steps") ### [Authentication](/api-info/indexing/authentication/overview.md) [Review Indexing API token permissions and datasource access.](/api-info/indexing/authentication/overview.md) ### [Troubleshooting](/api-info/indexing/debugging/datasource-config.md) [Diagnose datasource configuration and document visibility.](/api-info/indexing/debugging/datasource-config.md) ### [API Reference](/api/indexing-api.md#indexing-api-reference) [Browse Indexing API endpoints by capability.](/api/indexing-api.md#indexing-api-reference) --- # Setup Datasource To get started with the Indexing API, you first need to create a datasource that will store all the data you will be indexing. A datasource provides organizational structure and defines how your content appears in search results. ## Using the Glean Custom App Setup Page[​](#using-the-glean-custom-app-setup-page "Direct link to Using the Glean Custom App Setup Page") info Only Glean admins can set up custom apps using Glean's admin console.
If you are not a Glean admin, you can work with your Glean admin to set up a custom app or
follow Option 2 to set up a datasource using the `/adddatasource` API endpoint. To set up a custom app, admins can navigate to the admin console, then click on the [Data sources section](https://app.glean.com/admin/setup/apps). Click on the "Add data source" button in the top right corner. In the modal that appears, click on "Custom" at the bottom of the list. The key fields to quickly set up a datasource are listed under the "Data source basics" section. Some example values are shown in the screenshot below. ![Enable search results](/assets/images/custom_datasource_setup_example-35031cf719d0f846c208ecf1152d7102.png) Once the values are set, click on the "Publish" button to create the custom app. ## Using the `/adddatasource` API Endpoint[​](#using-the-adddatasource-api-endpoint "Direct link to using-the-adddatasource-api-endpoint") When creating a datasource, the key fields you need to set are the following: | Field | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | A unique identifier used to refer to the datasource. | | `displayName` | The datasource name shown in search results in the UI. | | `datasourceCategory` | The type of this datasource. This affects how results are ranked. More details on how to select a category can be inferred from [Datasource Categories](/api-info/indexing/datasource/category.md) documentation. | | `urlRegex` | A regex that captures the view URLs of documents in the datasource as accurately as possible. Avoid regexes that are too broad, and will capture URLs from other datasources, or regexes that are too narrow, and will not capture documents from this datasource. | | `isUserReferencedByEmail` | This should be set to true if you want to refer to user identities using emails directly. If you have your own notion of user ids, this can be set to false. This affects how Glean interprets permissions attached to documents. | * cURL * Python ``` curl -X POST https://customer-be.glean.com/api/index/v1/adddatasource \ -H 'Authorization: Bearer ' \ -d ' { "name": "gleantest", "displayName": "Glean Test", "datasourceCategory": "PUBLISHED_CONTENT", "urlRegex": "^https://bluesky.test.*", "objectDefinitions": [ { "name": "EngineeringDoc", "docCategory": "PUBLISHED_CONTENT" } ], "isUserReferencedByEmail": true }' ``` ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_INDEXING_API_TOKEN", ""), server_url=os.getenv("GLEAN_SERVER_URL", ""), ) as client: # Create a datasource try: client.indexing.datasources.add(request={ "name": "gleantest", "displayName": "Glean Test", "datasourceCategory": "PUBLISHED_CONTENT", "urlRegex": "^http://bluesky.test.*", "objectDefinitions": [ { "name": "EngineeringDoc", "docCategory": "PUBLISHED_CONTENT" } ], "isUserReferencedByEmail": True }) except Exception as e: print(f"Exception when creating datasource: {e}") ``` You can learn about more datasource customization options at [here](/api/indexing-api/add-or-update-datasource.md) For more information about datasource categories, see [Datasource Categories](/api-info/indexing/datasource/category.md). --- # Client API The Client API provides existing REST capabilities for building user-facing Glean experiences, including search, chat, agents, document access, and content management. When to choose Client API For new integrations, start with the [Platform API](/api/platform-api.md). Choose Client API when you need an existing Client API capability that is not yet available in Platform API or when you are maintaining an existing integration. ## Start here[​](#start-here "Direct link to Start here") ### [Quickstart](/api-info/client/getting-started/overview.md) [Make your first Client API request.](/api-info/client/getting-started/overview.md) ### [Authentication](/api-info/client/authentication/overview.md) [Choose and configure your Client API credentials.](/api-info/client/authentication/overview.md) ### [API Reference](#client-api-reference) [Browse Client API endpoints by capability.](#client-api-reference) ## Capabilities[​](#capabilities "Direct link to Capabilities") ### [Search and discovery](/api/client-api/search/overview.md) [Build custom search experiences over your organization's connected content.](/api/client-api/search/overview.md) ### [Chat](/api/client-api/chat/overview.md) [Create conversational experiences grounded in Glean knowledge.](/api/client-api/chat/overview.md) ### [Agents](/api/client-api/agents/overview.md) [Discover and run agents available to the authenticated user.](/api/client-api/agents/overview.md) ### [Documents](/api/client-api/documents/overview.md) [Retrieve documents and work with document permissions.](/api/client-api/documents/overview.md) ## Client API reference[​](#client-api-reference "Direct link to Client API reference") Use the capability groups in the sidebar to find endpoint details, request parameters, schemas, and examples. * [ActivityReport document and search activity to improve relevance and recommendations.2](/api/client-api/activity/overview.md) [ ](/api/client-api/activity/overview.md) [endpoints](/api/client-api/activity/overview.md) * [AgentsDiscover, create, and run Glean agents from your applications.8](/api/client-api/agents/overview.md) [ ](/api/client-api/agents/overview.md) [endpoints](/api/client-api/agents/overview.md) * [AnnouncementsCreate and manage targeted organizational announcements.3](/api/client-api/announcements/overview.md) [ ](/api/client-api/announcements/overview.md) [endpoints](/api/client-api/announcements/overview.md) * [AnswersCreate and manage curated answers to frequently asked questions.5](/api/client-api/answers/overview.md) [ ](/api/client-api/answers/overview.md) [endpoints](/api/client-api/answers/overview.md) * [AuthenticationCreate authentication tokens and verify datasource authorization.2](/api/client-api/authentication/overview.md) [ ](/api/client-api/authentication/overview.md) [endpoints](/api/client-api/authentication/overview.md) * [ChatBuild conversational experiences, manage chat history, and work with chat files.11](/api/client-api/chat/overview.md) [ ](/api/client-api/chat/overview.md) [endpoints](/api/client-api/chat/overview.md) * [DocumentsRetrieve document metadata and inspect document permissions.3](/api/client-api/documents/overview.md) [ ](/api/client-api/documents/overview.md) [endpoints](/api/client-api/documents/overview.md) * [CollectionsCreate and manage curated collections of documents and resources.8](/api/client-api/collections/overview.md) [ ](/api/client-api/collections/overview.md) [endpoints](/api/client-api/collections/overview.md) * [EntitiesRetrieve people and other structured entities from your Glean instance.3](/api/client-api/entities/overview.md) [ ](/api/client-api/entities/overview.md) [endpoints](/api/client-api/entities/overview.md) * [GovernanceManage governance policies, reports, document visibility, and findings exports.14](/api/client-api/governance/overview.md) [ ](/api/client-api/governance/overview.md) [endpoints](/api/client-api/governance/overview.md) * [InsightsRetrieve aggregated usage insights about users, queries, and content.1](/api/client-api/insights/overview.md) [ ](/api/client-api/insights/overview.md) [endpoint](/api/client-api/insights/overview.md) * [MessagesRetrieve messages from connected chat and messaging platforms.1](/api/client-api/messages/overview.md) [ ](/api/client-api/messages/overview.md) [endpoint](/api/client-api/messages/overview.md) * [PinsCreate and manage pinned results for important search queries.5](/api/client-api/pins/overview.md) [ ](/api/client-api/pins/overview.md) [endpoints](/api/client-api/pins/overview.md) * [SearchSearch connected content, autocomplete queries, and retrieve personalized feeds.5](/api/client-api/search/overview.md) [ ](/api/client-api/search/overview.md) [endpoints](/api/client-api/search/overview.md) * [ShortcutsCreate and manage shortcuts to frequently used queries and resources.5](/api/client-api/shortcuts/overview.md) [ ](/api/client-api/shortcuts/overview.md) [endpoints](/api/client-api/shortcuts/overview.md) * [SummarizeGenerate concise AI summaries of documents and organizational content.1](/api/client-api/summarize/overview.md) [ ](/api/client-api/summarize/overview.md) [endpoint](/api/client-api/summarize/overview.md) * [ToolsDiscover and execute tools and authorize external integrations.7](/api/client-api/tools/overview.md) [ ](/api/client-api/tools/overview.md) [endpoints](/api/client-api/tools/overview.md) * [VerificationCreate and manage content verification reminders and workflows.3](/api/client-api/verification/overview.md) [ ](/api/client-api/verification/overview.md) [endpoints](/api/client-api/verification/overview.md) --- # Report document activity * Endpoint ``` POST /rest/api/v1/activity ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Report user activity that occurs on indexed documents such as viewing or editing. This signal improves search quality. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Report client activity * Endpoint ``` POST /rest/api/v1/feedback ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Report events that happen to results within a Glean client UI, such as search result views and clicks. This signal improves search quality. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Activity API The Activity API enables you to report user activity on indexed documents and search interactions to improve Glean's search quality and relevance. By tracking document views, edits, searches, and other user interactions, this API helps Glean understand content usage patterns and optimize search results. ## Use Cases ### Document Activity Tracking Report user interactions with documents such as viewing, editing, and commenting to improve search relevance and content recommendations. ### Search Feedback Collection Capture search result interactions, clicks, and user feedback to enhance search quality and result ranking algorithms. ### Usage Analytics Gather comprehensive usage data to understand content popularity, user engagement patterns, and optimize the search experience. ## API Endpoints | Method | Endpoint | Purpose | | ------ | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/activity`](/api/client-api/activity/activity.md) | [Report document activity](/api/client-api/activity/activity.md)Report user activity that occurs on indexed documents such as viewing or editing. This signal improves search quality. | | POST | [`/rest/api/v1/feedback`](/api/client-api/activity/feedback.md) | [Report client activity](/api/client-api/activity/feedback.md)Report events that happen to results within a Glean client UI, such as search result views and clicks. This signal improves search quality. | --- # Create an agent * Endpoint ``` POST /rest/api/v1/agents ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Create an agent. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 500 Success Bad request Not Authorized Forbidden Internal server error --- # Create an agent run and stream the response * Endpoint ``` POST /rest/api/v1/agents/runs/stream ``` * Release stage DeprecatedUse POST /api/agents/{agent\_id}/runs with stream=true instead. · Removal on April 15, 2027 · [Migration guide](https://developers.glean.com/api/platform-api/agents-overview) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Executes an [agent](https://developers.glean.com/agents/agents-api) run and returns the result as a stream of server-sent events (SSE). **Note**: If the agent uses an input form trigger, all form fields (including optional fields) must be included in the `input` object. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 403 * 404 * 409 * 422 * 500 Success Bad request Forbidden Not Found Conflict Returned when the agent has tools the caller has not authorized. The response body enumerates the tools that need connecting. Internal server error --- # Create an agent run and wait for the response * Endpoint ``` POST /rest/api/v1/agents/runs/wait ``` * Release stage DeprecatedUse POST /api/agents/{agent\_id}/runs instead. · Removal on April 15, 2027 · [Migration guide](https://developers.glean.com/api/platform-api/agents-overview) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Executes an [agent](https://developers.glean.com/agents/agents-api) run and returns the final response. **Note**: If the agent uses an input form trigger, all form fields (including optional fields) must be included in the `input` object. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 403 * 404 * 409 * 422 * 500 Success Bad request Forbidden Not Found Conflict Returned when the agent has tools the caller has not authorized. The response body enumerates the tools that need connecting. Internal server error --- # Edit an agent * Endpoint ``` POST /rest/api/v1/agents/:agent_id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Creates a draft or publishes an [agent](https://developers.glean.com/agents/agents-api). Use `isDraft=true` to save a draft, or `isDraft=false` (or omit) to publish immediately. Only draft and publish modes are supported. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 500 Success Bad request Not Authorized Forbidden Not Found Internal server error --- # Retrieve an agent * Endpoint ``` GET /rest/api/v1/agents/:agent_id ``` * Release stage DeprecatedUse GET /api/agents/{agent\_id} instead. · Removal on April 15, 2027 · [Migration guide](https://developers.glean.com/api/platform-api/agents-overview) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Returns details of an [agent](https://developers.glean.com/agents/agents-api) created in the Agent Builder. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 403 * 404 * 500 Success Bad request Forbidden Not Found Internal server error --- # List an agent's schemas * Endpoint ``` GET /rest/api/v1/agents/:agent_id/schemas ``` * Release stage DeprecatedUse GET /api/agents/{agent\_id}/schemas instead. · Removal on April 15, 2027 · [Migration guide](https://developers.glean.com/api/platform-api/agents-overview) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Return [agent](https://developers.glean.com/agents/agents-api)'s input and output schemas. You can use these schemas to detect changes to an agent's input or output structure. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 403 * 404 * 422 * 500 Success Bad request Forbidden Not Found Validation Error Internal server error --- # Import an agent * Endpoint ``` POST /rest/api/v1/agents/:agent_id/import ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Imports an [agent](https://developers.glean.com/agents/agents-api) from its on-disk folder representation (spec.yaml, instructions.md, skills/, subagents/) packaged as a zip, and creates or updates the agent. Inverse of the export flow: the folder-to-schema conversion runs server-side. The bundle must contain only regular files; symlinks are resolved by the caller at packaging time. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 500 Success Bad request Not Authorized Forbidden Not Found Internal server error --- # Agents API Overview The Agents API provides programmatic control over Glean agents, enabling you to build, deploy, and manage AI agents across your organization. This API allows you to integrate Glean's agent capabilities directly into your applications and workflows. ## Use Cases ### Agent Discovery Search and retrieve available agents in your deployment to understand what capabilities are available for integration. ### Agent Execution Create and manage agent runs with streaming or blocking responses for real-time chat interfaces and batch processing. ### Schema Inspection Examine agent input and output schemas for integration planning and validation in your applications. ### Real-time Integration Stream agent responses to chat interfaces and applications for immediate user feedback and interaction. ### External Application Integration Embed Glean agents into your existing systems and workflows for seamless AI-powered assistance. ## API Endpoints | Method | Endpoint | Purpose | | ------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/agents`](/api/client-api/agents/create-agent.md) | [Create an agent](/api/client-api/agents/create-agent.md)Create an agent. | | POST | [`/rest/api/v1/agents/runs/stream`](/api/client-api/agents/create-and-stream-run.md) | [Create an agent run and stream the response](/api/client-api/agents/create-and-stream-run.md)Executes an \[agent]\(https\://developers.glean.com/agents/agents-api) run and returns the result as a stream of server-sent events (SSE). \*\*Note\*\*: If the agent uses an input form trigger, all form fields (including optional fields) must be included in the \`input\` object. | | POST | [`/rest/api/v1/agents/runs/wait`](/api/client-api/agents/create-and-wait-run.md) | [Create an agent run and wait for the response](/api/client-api/agents/create-and-wait-run.md)Executes an \[agent]\(https\://developers.glean.com/agents/agents-api) run and returns the final response. \*\*Note\*\*: If the agent uses an input form trigger, all form fields (including optional fields) must be included in the \`input\` object. | | POST | [`/rest/api/v1/agents/search`](/api/client-api/agents/search-agents.md) | [Search agents](/api/client-api/agents/search-agents.md)Search for \[agents]\(https\://developers.glean.com/agents/agents-api) by agent name. | | GET | [`/rest/api/v1/agents/{agent_id}`](/api/client-api/agents/get-agent.md) | [Retrieve an agent](/api/client-api/agents/get-agent.md)Returns details of an \[agent]\(https\://developers.glean.com/agents/agents-api) created in the Agent Builder. | | POST | [`/rest/api/v1/agents/{agent_id}`](/api/client-api/agents/edit-agent.md) | [Edit an agent](/api/client-api/agents/edit-agent.md)Creates a draft or publishes an \[agent]\(https\://developers.glean.com/agents/agents-api). Use \`isDraft=true\` to save a draft, or \`isDraft=false\` (or omit) to publish immediately. Only draft and publish modes are supported. | | POST | [`/rest/api/v1/agents/{agent_id}/import`](/api/client-api/agents/import-agent.md) | [Import an agent](/api/client-api/agents/import-agent.md)Imports an \[agent]\(https\://developers.glean.com/agents/agents-api) from its on-disk folder representation (spec.yaml, instructions.md, skills/, subagents/) packaged as a zip, and creates or updates the agent. Inverse of the export flow: the folder-to-schema conversion runs server-side. The bundle must contain only regular files; symlinks are resolved by the caller at packaging time. | | GET | [`/rest/api/v1/agents/{agent_id}/schemas`](/api/client-api/agents/get-agent-schemas.md) | [List an agent's schemas](/api/client-api/agents/get-agent-schemas.md)Return \[agent]\(https\://developers.glean.com/agents/agents-api)'s input and output schemas. You can use these schemas to detect changes to an agent's input or output structure. | ## Agent Builder Integration[​](#agent-builder-integration "Direct link to Agent Builder Integration") Agents are created and configured through Glean's Agent Builder interface. The API provides programmatic access to these user-created agents. ![Agent Builder Interface](/img/agents/agent-builder.png) ### Finding Agent IDs[​](#finding-agent-ids "Direct link to Finding Agent IDs") To use an agent programmatically, you'll need its Agent ID. You can find this in the Agent Builder URL: ![Agent ID in URL](/img/agents/agent-id.png) The Agent ID appears in the URL path: `/admin/agents/{agentId}` ## Protocol Compatibility[​](#protocol-compatibility "Direct link to Protocol Compatibility") The Agents API implements a subset of the [LangChain Agent Protocol](https://langchain-ai.github.io/agent-protocol/api.html#tag/agents), specifically: * **[Agents](https://langchain-ai.github.io/agent-protocol/api.html#tag/agents)**: Agent discovery and metadata * **[Runs](https://langchain-ai.github.io/agent-protocol/api.html#tag/runs)**: Agent execution and run management This ensures compatibility with any agent runtime that supports this standard protocol. ## Authentication & Setup[​](#authentication--setup "Direct link to Authentication & Setup") Before using the Agents API, ensure you have: 1. **API Token**: A [user-scoped API token](/api-info/client/authentication/glean-issued.md#available-scopes) with `agents` scope 2. **Agent Access**: Permissions to access the agents you want to use 3. **Base URL**: Your organization's Glean instance URL ## Common Integration Patterns[​](#common-integration-patterns "Direct link to Common Integration Patterns") ### Streaming Responses[​](#streaming-responses "Direct link to Streaming Responses") For real-time chat interfaces, use the streaming endpoints to provide immediate feedback: ``` const response = await fetch('/agents/agent-123/runs', { method: 'POST', headers: { 'Authorization': 'Bearer your-token' }, body: JSON.stringify({ query: 'What is our vacation policy?', stream: true }) }); const reader = response.body.getReader(); // Process streaming chunks... ``` ### Batch Processing[​](#batch-processing "Direct link to Batch Processing") For bulk operations, use the blocking endpoints to process multiple queries: ``` import asyncio from glean.api_client import Glean async def process_queries(queries): with Glean(api_token="your-token", instance="your-org") as client: tasks = [ client.agents.create_and_wait_run( agent_id="agent-123", query=query ) for query in queries ] return await asyncio.gather(*tasks) ``` ## Next Steps[​](#next-steps "Direct link to Next Steps") * Browse the API endpoint documentation above for detailed request/response schemas * Check out the [Building Agents guide](/guides/agents/overview.md) for different ways to create agent-powered applications * Review [authentication requirements](/api-info/client/authentication/overview.md) for API access --- # Search agents * Endpoint ``` POST /rest/api/v1/agents/search ``` * Release stage DeprecatedUse POST /api/agents/search instead. · Removal on April 15, 2027 · [Migration guide](https://developers.glean.com/api/platform-api/agents-overview) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Search for [agents](https://developers.glean.com/agents/agents-api) by agent name. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 403 * 404 * 422 * 500 Success Bad request Forbidden Not Found Validation Error Internal server error --- # Create Announcement * Endpoint ``` POST /rest/api/v1/createannouncement ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) * Deprecations 8 deprecated fields · earliest removal October 15, 2026 `isNegated` Use relationType instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `documents` Field is deprecated Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `boardId` Answer Boards no longer supported Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `snippet` Use 'text' field instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `inviteTime` Use ChannelInviteInfo instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `reminderTime` Use ChannelInviteInfo instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `reactions` Use reacts instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `role` Use permissions instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 Create a textual announcement visible to some set of users based on department and location. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Delete Announcement * Endpoint ``` POST /rest/api/v1/deleteannouncement ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Delete an existing user-generated announcement. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Announcements API The Announcements API allows administrators to create, update, and manage organizational announcements that are visible to users based on department and location targeting. These announcements help keep teams informed about important updates, policy changes, and company-wide communications. ## Use Cases ### Company-wide Communications Create and broadcast important announcements to all employees or specific departments about policy changes, events, and updates. ### Department-specific Notifications Target announcements to specific departments or locations to ensure relevant information reaches the right audience. ### Announcement Management Update, modify, or remove announcements as needed to keep organizational communications current and accurate. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/createannouncement`](/api/client-api/announcements/createannouncement.md) | [Create Announcement](/api/client-api/announcements/createannouncement.md)Create a textual announcement visible to some set of users based on department and location. | | POST | [`/rest/api/v1/deleteannouncement`](/api/client-api/announcements/deleteannouncement.md) | [Delete Announcement](/api/client-api/announcements/deleteannouncement.md)Delete an existing user-generated announcement. | | POST | [`/rest/api/v1/updateannouncement`](/api/client-api/announcements/updateannouncement.md) | [Update Announcement](/api/client-api/announcements/updateannouncement.md)Update a textual announcement visible to some set of users based on department and location. | --- # Update Announcement * Endpoint ``` POST /rest/api/v1/updateannouncement ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Update a textual announcement visible to some set of users based on department and location. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Create Answer * Endpoint ``` POST /rest/api/v1/createanswer ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Create a user-generated Answer that contains a question and answer. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Delete Answer * Endpoint ``` POST /rest/api/v1/deleteanswer ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Delete an existing user-generated Answer. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Update Answer * Endpoint ``` POST /rest/api/v1/editanswer ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Update an existing user-generated Answer. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Read Answer * Endpoint ``` POST /rest/api/v1/getanswer ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) * Deprecations 1 deprecated field · earliest removal January 15, 2027 `trackingToken` Use \`answer.trackingToken\` instead. Deprecation introduced on 2026-05-07|Will be removed after 2027-01-15 Read the details of a particular Answer given its ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # List Answers * Endpoint ``` POST /rest/api/v1/listanswers ``` * Release stage DeprecatedAnswer boards have been removed and this endpoint no longer serves a purpose · Removal on October 15, 2026 · [All deprecations](/deprecations/.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) List Answers created by the current user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Answers API The Answers API enables you to create, manage, and retrieve user-generated answers to frequently asked questions. These answers help build a comprehensive knowledge base that improves search results and provides instant responses to common queries within your organization. ## Use Cases ### Knowledge Base Creation Build and maintain a comprehensive repository of questions and answers to help users find information quickly and efficiently. ### FAQ Management Create, update, and organize frequently asked questions with detailed answers to reduce support tickets and improve self-service. ### Search Enhancement Improve search results by providing curated answers that appear prominently when users search for related topics. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/createanswer`](/api/client-api/answers/createanswer.md) | [Create Answer](/api/client-api/answers/createanswer.md)Create a user-generated Answer that contains a question and answer. | | POST | [`/rest/api/v1/deleteanswer`](/api/client-api/answers/deleteanswer.md) | [Delete Answer](/api/client-api/answers/deleteanswer.md)Delete an existing user-generated Answer. | | POST | [`/rest/api/v1/editanswer`](/api/client-api/answers/editanswer.md) | [Update Answer](/api/client-api/answers/editanswer.md)Update an existing user-generated Answer. | | POST | [`/rest/api/v1/getanswer`](/api/client-api/answers/getanswer.md) | [Read Answer](/api/client-api/answers/getanswer.md)Read the details of a particular Answer given its ID. | | POST | [`/rest/api/v1/listanswers`](/api/client-api/answers/listanswers.md) | [List Answers](/api/client-api/answers/listanswers.md)List Answers created by the current user. | --- # Check datasource authorization * Endpoint ``` POST /rest/api/v1/checkdatasourceauth ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Returns all datasource instances that require per-user OAuth authorization for the authenticated user, along with a transient auth token that can be appended to auth URLs to complete OAuth flows. Clients construct the full OAuth URL by combining the backend base URL, the `authUrlRelativePath` from each instance, and the transient auth token: `/?transient_auth_token=`. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 401 * 429 OK Not Authorized Too Many Requests --- # Create authentication token * Endpoint ``` POST /rest/api/v1/createauthtoken ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Creates an authentication token for the authenticated user. These are specifically intended to be used with the [Web SDK](https://developers.glean.com/web). Note: The tokens generated from this endpoint are **not** valid tokens for use with the Client API (e.g. `/rest/api/v1/*`). ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid Request Not Authorized Too Many Requests --- # Authentication API The Authentication API provides secure token-based authentication for accessing Glean's APIs. It enables you to create and manage authentication tokens that authorize your applications to interact with Glean services on behalf of users. ## Use Cases ### API Access Control Generate secure authentication tokens to authorize your applications and services to access Glean APIs with proper user permissions. ### Service Integration Enable third-party applications and internal services to securely integrate with Glean using token-based authentication. ### User Authorization Create user-specific tokens that respect individual permissions and access controls within your organization. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/checkdatasourceauth`](/api/client-api/authentication/checkdatasourceauth.md) | [Check datasource authorization](/api/client-api/authentication/checkdatasourceauth.md)Returns all datasource instances that require per-user OAuth authorization for the authenticated user, along with a transient auth token that can be appended to auth URLs to complete OAuth flows. Clients construct the full OAuth URL by combining the backend base URL, the \`authUrlRelativePath\` from each instance, and the transient auth token: \`\/\?transient\_auth\_token=\\`. | | POST | [`/rest/api/v1/createauthtoken`](/api/client-api/authentication/createauthtoken.md) | [Create authentication token](/api/client-api/authentication/createauthtoken.md)Creates an authentication token for the authenticated user. These are specifically intended to be used with the \[Web SDK]\(https\://developers.glean.com/web). Note: The tokens generated from this endpoint are \*\*not\*\* valid tokens for use with the Client API (e.g. \`/rest/api/v1/\*\`). | --- # Chat * Endpoint ``` POST /rest/api/v1/chat ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) * Deprecations 1 deprecated field · earliest removal October 15, 2026 `citations` Use inline citations via ChatMessageFragment.citation and ChatMessageCitation.referenceRanges instead. This field is still populated for backward compatibility. Deprecation introduced on 2026-02-06|Will be removed after 2026-10-15 Have a conversation with Glean AI. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 202 * 400 * 401 * 408 * 429 OK Request accepted but not yet processed. Returned when another in-flight request is already running for the same chat session; the body's `queuedRequestId` identifies the deferred run and output will be persisted to the chat session referenced by `chatId`. Invalid request Not Authorized Request Timeout Too Many Requests --- # Chat * Endpoint ``` POST /rest/api/v1/chat#stream ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Have a conversation with Glean AI. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 408 * 429 OK Invalid request Not Authorized Request Timeout Too Many Requests --- # Deletes all saved Chats owned by a user * Endpoint ``` POST /rest/api/v1/deleteallchats ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Deletes all saved Chats a user has had and all their contained conversational content. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 OK Invalid request Not Authorized Forbidden --- # Delete files uploaded by a user for chat * Endpoint ``` POST /rest/api/v1/deletechatfiles ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Delete files uploaded by a user for Chat. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Forbidden Too Many Requests --- # Deletes saved Chats * Endpoint ``` POST /rest/api/v1/deletechats ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Deletes saved Chats and all their contained conversational content. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Forbidden Too Many Requests --- # Download a chat file * Endpoint ``` GET /rest/api/v1/chat-files/:fileId ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Download the raw content of a file generated or uploaded during a chat session (for example, an image produced by the assistant). Returns the file bytes with a Content-Type header matching the file's MIME type. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 500 File content. File ID missing from path. Missing or invalid API token. Caller does not have access to the file. File not found. Internal server error. --- # Retrieves a Chat * Endpoint ``` POST /rest/api/v1/getchat ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Retrieves the chat history between Glean Assistant and the user for a given Chat. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Forbidden Too Many Requests --- # Gets the metadata for a custom Chat application * Endpoint ``` POST /rest/api/v1/getchatapplication ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Gets the Chat application details for the specified application ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 OK Invalid request Not Authorized Forbidden --- # Get files uploaded by a user for Chat * Endpoint ``` POST /rest/api/v1/getchatfiles ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Get files uploaded by a user for Chat. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Forbidden Too Many Requests --- # Retrieves all saved Chats * Endpoint ``` POST /rest/api/v1/listchats ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Retrieves all the saved Chats between Glean Assistant and the user. The returned Chats contain only metadata and no conversational content. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 401 * 403 * 429 OK Not Authorized Forbidden Too Many Requests --- # Chat API The Chat API enables you to build conversational experiences with Glean AI. Create interactive chat applications, manage conversation history, upload files for context, and leverage Glean's AI capabilities to provide intelligent responses based on your organization's knowledge. ## Use Cases ### AI-Powered Conversations Build interactive chat applications that leverage Glean AI to provide intelligent responses based on your organization's indexed content. ### File-Enhanced Context Upload and manage files within chat conversations to provide additional context for more accurate and relevant AI responses. ### Conversation Management Create, retrieve, and manage chat histories to maintain context across sessions and provide personalized user experiences. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/chat#stream`](/api/client-api/chat/chat-stream.md) | [Chat](/api/client-api/chat/chat-stream.md)Have a conversation with Glean AI. | | POST | [`/rest/api/v1/chat`](/api/client-api/chat/chat.md) | [Chat](/api/client-api/chat/chat.md)Have a conversation with Glean AI. | | GET | [`/rest/api/v1/chat-files/{fileId}`](/api/client-api/chat/get-chat-file.md) | [Download a chat file](/api/client-api/chat/get-chat-file.md)Download the raw content of a file generated or uploaded during a chat session (for example, an image produced by the assistant). Returns the file bytes with a Content-Type header matching the file's MIME type. | | POST | [`/rest/api/v1/deleteallchats`](/api/client-api/chat/deleteallchats.md) | [Deletes all saved Chats owned by a user](/api/client-api/chat/deleteallchats.md)Deletes all saved Chats a user has had and all their contained conversational content. | | POST | [`/rest/api/v1/deletechatfiles`](/api/client-api/chat/deletechatfiles.md) | [Delete files uploaded by a user for chat](/api/client-api/chat/deletechatfiles.md)Delete files uploaded by a user for Chat. | | POST | [`/rest/api/v1/deletechats`](/api/client-api/chat/deletechats.md) | [Deletes saved Chats](/api/client-api/chat/deletechats.md)Deletes saved Chats and all their contained conversational content. | | POST | [`/rest/api/v1/getchat`](/api/client-api/chat/getchat.md) | [Retrieves a Chat](/api/client-api/chat/getchat.md)Retrieves the chat history between Glean Assistant and the user for a given Chat. | | POST | [`/rest/api/v1/getchatapplication`](/api/client-api/chat/getchatapplication.md) | [Gets the metadata for a custom Chat application](/api/client-api/chat/getchatapplication.md)Gets the Chat application details for the specified application ID. | | POST | [`/rest/api/v1/getchatfiles`](/api/client-api/chat/getchatfiles.md) | [Get files uploaded by a user for Chat](/api/client-api/chat/getchatfiles.md)Get files uploaded by a user for Chat. | | POST | [`/rest/api/v1/listchats`](/api/client-api/chat/listchats.md) | [Retrieves all saved Chats](/api/client-api/chat/listchats.md)Retrieves all the saved Chats between Glean Assistant and the user. The returned Chats contain only metadata and no conversational content. | | POST | [`/rest/api/v1/uploadchatfiles`](/api/client-api/chat/uploadchatfiles.md) | [Upload files for Chat](/api/client-api/chat/uploadchatfiles.md)Upload files for Chat. | --- # Upload files for Chat * Endpoint ``` POST /rest/api/v1/uploadchatfiles ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Upload files for Chat. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Forbidden Too Many Requests --- # Add Collection item * Endpoint ``` POST /rest/api/v1/addcollectionitems ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Add items to a Collection. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Create Collection * Endpoint ``` POST /rest/api/v1/createcollection ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Create a publicly visible (empty) Collection of documents. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 422 * 429 OK Invalid request Not Authorized Semantic error Too Many Requests --- # Delete Collection * Endpoint ``` POST /rest/api/v1/deletecollection ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Delete a Collection given the Collection's ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 422 * 429 OK Invalid request Not Authorized Semantic error Too Many Requests --- # Delete Collection item * Endpoint ``` POST /rest/api/v1/deletecollectionitem ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Delete a single item from a Collection. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 422 * 429 OK Invalid request Not Authorized Failed to save deletion Too Many Requests --- # Update Collection * Endpoint ``` POST /rest/api/v1/editcollection ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Update the properties of an existing Collection. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 422 * 429 OK Invalid request Not Authorized Semantic error Too Many Requests --- # Update Collection item * Endpoint ``` POST /rest/api/v1/editcollectionitem ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Update the URL, Glean Document ID, description of an item within a Collection given its ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Read Collection * Endpoint ``` POST /rest/api/v1/getcollection ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) * Deprecations 1 deprecated field · earliest removal January 15, 2027 `trackingToken` Use \`collection.trackingToken\` instead. Deprecation introduced on 2026-05-07|Will be removed after 2027-01-15 Read the details of a Collection given its ID. Does not fetch items in this Collection. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # List Collections * Endpoint ``` POST /rest/api/v1/listcollections ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) List all existing Collections. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Collections API The Collections API allows you to create and manage curated collections of documents and resources. Organize related content into themed collections, making it easier for users to discover and access relevant information within your organization. ## Use Cases ### Content Curation Create organized collections of related documents, resources, and links to help users discover relevant content more efficiently. ### Knowledge Organization Group documents by topic, project, or department to create structured knowledge repositories that improve content discoverability. ### Resource Management Add, update, and remove items from collections to keep curated content current and maintain high-quality resource lists. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/addcollectionitems`](/api/client-api/collections/addcollectionitems.md) | [Add Collection item](/api/client-api/collections/addcollectionitems.md)Add items to a Collection. | | POST | [`/rest/api/v1/createcollection`](/api/client-api/collections/createcollection.md) | [Create Collection](/api/client-api/collections/createcollection.md)Create a publicly visible (empty) Collection of documents. | | POST | [`/rest/api/v1/deletecollection`](/api/client-api/collections/deletecollection.md) | [Delete Collection](/api/client-api/collections/deletecollection.md)Delete a Collection given the Collection's ID. | | POST | [`/rest/api/v1/deletecollectionitem`](/api/client-api/collections/deletecollectionitem.md) | [Delete Collection item](/api/client-api/collections/deletecollectionitem.md)Delete a single item from a Collection. | | POST | [`/rest/api/v1/editcollection`](/api/client-api/collections/editcollection.md) | [Update Collection](/api/client-api/collections/editcollection.md)Update the properties of an existing Collection. | | POST | [`/rest/api/v1/editcollectionitem`](/api/client-api/collections/editcollectionitem.md) | [Update Collection item](/api/client-api/collections/editcollectionitem.md)Update the URL, Glean Document ID, description of an item within a Collection given its ID. | | POST | [`/rest/api/v1/getcollection`](/api/client-api/collections/getcollection.md) | [Read Collection](/api/client-api/collections/getcollection.md)Read the details of a Collection given its ID. Does not fetch items in this Collection. | | POST | [`/rest/api/v1/listcollections`](/api/client-api/collections/listcollections.md) | [List Collections](/api/client-api/collections/listcollections.md)List all existing Collections. | --- # Read document permissions * Endpoint ``` POST /rest/api/v1/getdocpermissions ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Read the emails of all users who have access to the given document. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Forbidden Too Many Requests --- # Read documents * Endpoint ``` POST /rest/api/v1/getdocuments ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Read the documents including metadata (does not include enhanced metadata via `/documentmetadata`) for the given list of Glean Document IDs or URLs specified in the request. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Documents does not exist, or user cannot access documents. Too Many Requests --- # Read documents by facets * Endpoint ``` POST /rest/api/v1/getdocumentsbyfacets ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Read the documents including metadata (does not include enhanced metadata via `/documentmetadata`) macthing the given facet conditions. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 OK Invalid request Not Authorized Not Found Too Many Requests --- # Documents API The Documents API provides access to document metadata, permissions, and content within your organization. Retrieve document information, check access permissions, and query documents by various criteria to build powerful document management and discovery features. ## Use Cases ### Document Discovery Retrieve document metadata and content to build custom search and discovery interfaces that help users find relevant information. ### Permission Management Check document access permissions to ensure users only see content they're authorized to access in your applications. ### Content Analysis Access document metadata and facets to perform content analysis, categorization, and build insights about your organization's knowledge. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | POST | [`/rest/api/v1/getdocpermissions`](/api/client-api/documents/getdocpermissions.md) | [Read document permissions](/api/client-api/documents/getdocpermissions.md)Read the emails of all users who have access to the given document. | | POST | [`/rest/api/v1/getdocuments`](/api/client-api/documents/getdocuments.md) | [Read documents](/api/client-api/documents/getdocuments.md)Read the documents including metadata (does not include enhanced metadata via \`/documentmetadata\`) for the given list of Glean Document IDs or URLs specified in the request. | | POST | [`/rest/api/v1/getdocumentsbyfacets`](/api/client-api/documents/getdocumentsbyfacets.md) | [Read documents by facets](/api/client-api/documents/getdocumentsbyfacets.md)Read the documents including metadata (does not include enhanced metadata via \`/documentmetadata\`) macthing the given facet conditions. | --- # Get person photo * Endpoint ``` GET /rest/api/v1/people/:person_id/photo ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Returns the profile photo bytes for a person whose photo is stored in Glean (crawled from an identity source or user-uploaded via admin console). Photos hosted externally (e.g. Slack CDN) are not served by this endpoint; callers should follow the photoUrl from /people or /listentities directly. Responses include a Cache-Control header (max-age=3600) to reduce redundant fetches. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 Photo bytes returned successfully. **Response Headers** * **Cache-Control** Caching directive for the photo response. Missing person\_id parameter. Not Authorized. Person not found, person has no photo, or photo is not hosted by Glean (follow photoUrl from /people or /listentities directly). Too Many Requests. --- # List entities * Endpoint ``` POST /rest/api/v1/listentities ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) List some set of details for all entities that fit the given criteria and return in the requested order. Does not support negation in filters, assumes relation type EQUALS. There is a limit of 10000 entities that can be retrieved via this endpoint, except when using FULL\_DIRECTORY request type for people entities. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Entities API The Entities API provides access to organizational entities such as people, teams, and other structured data within your Glean instance. Query and retrieve entity information to build directory services, organizational charts, and people-centric applications. ## Use Cases ### People Directory Build employee directories and organizational charts by accessing people entities with their profiles, roles, and contact information. ### Team Management Retrieve team and organizational structure information to create team-based applications and workflow management tools. ### Entity Search Search across different entity types to find people, teams, and other organizational resources for integration into your applications. ## API Endpoints | Method | Endpoint | Purpose | | ------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | POST | [`/rest/api/v1/listentities`](/api/client-api/entities/listentities.md) | [List entities](/api/client-api/entities/listentities.md)List some set of details for all entities that fit the given criteria and return in the requested order. Does not support negation in filters, assumes relation type EQUALS. There is a limit of 10000 entities that can be retrieved via this endpoint, except when using FULL\_DIRECTORY request type for people entities. | | POST | [`/rest/api/v1/people`](/api/client-api/entities/people.md) | [Read people](/api/client-api/entities/people.md)Read people details for the given IDs. | | GET | [`/rest/api/v1/people/{person_id}/photo`](/api/client-api/entities/get-person-photo.md) | [Get person photo](/api/client-api/entities/get-person-photo.md)Returns the profile photo bytes for a person whose photo is stored in Glean (crawled from an identity source or user-uploaded via admin console). Photos hosted externally (e.g. Slack CDN) are not served by this endpoint; callers should follow the photoUrl from /people or /listentities directly. Responses include a Cache-Control header (max-age=3600) to reduce redundant fetches. | --- # Read people * Endpoint ``` POST /rest/api/v1/people ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Read people details for the given IDs. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Creates findings export * Endpoint ``` POST /rest/api/v1/governance/data/findings/exports ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Creates a new DLP findings export job. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Creates new policy * Endpoint ``` POST /rest/api/v1/governance/data/policies ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Creates a new policy with specified specifications and returns its id. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Creates new one-time report * Endpoint ``` POST /rest/api/v1/governance/data/reports ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Creates a new one-time report and executes its batch job. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Deletes findings export * Endpoint ``` DELETE /rest/api/v1/governance/data/findings/exports/:id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Deletes a DLP findings export. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Downloads findings export * Endpoint ``` GET /rest/api/v1/governance/data/findings/exports/:id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Downloads a DLP findings export as a CSV file. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 Downloads CSV of exported findings. Permissions error Internal error --- # Downloads violations CSV for policy * Endpoint ``` GET /rest/api/v1/governance/data/policies/:id/download ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Downloads CSV violations report for a specific policy id. This does not support continuous policies. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 403 * 500 Downloads csv of batch policy violations. Bad request error (e.g., continuous policies are not supported). Permissions error Internal error --- # Downloads violations CSV for report * Endpoint ``` GET /rest/api/v1/governance/data/reports/:id/download ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Downloads CSV violations report for a specific report id. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 Downloads csv of one-time report violations. Permissions error Internal error --- # Fetches documents visibility * Endpoint ``` GET /rest/api/v1/governance/documents/visibilityoverrides ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Fetches the visibility override status of the documents passed. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 The visibility status of documents Permissions error Internal error --- # Gets specified policy * Endpoint ``` GET /rest/api/v1/governance/data/policies/:id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) * Deprecations 10 deprecated fields · earliest removal October 15, 2026 `sensitiveInfoTypes` Use sensitiveContentOptions instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `likelihoodThreshold` Field is deprecated Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `urlGreenlist` Field is deprecated Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `datasources` Use datasourceInstances instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `externalSharingOptions` Use broadSharingOptions instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `enabled` Field is deprecated Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `anyoneWithLinkEnabled` Field is deprecated Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `sensitiveInfoTypes` Use customSensitiveExpressions instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `sensitiveTerms` Use customSensitiveExpressions instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 `sensitiveRegexes` Use customSensitiveExpressions instead Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 Fetches the specified policy version, or the latest if no version is provided. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Fetches report run status * Endpoint ``` GET /rest/api/v1/governance/data/reports/:id/status ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Fetches the status of the run corresponding to the report-id. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 Fetches status of report run. Permissions error Internal error --- # Lists findings exports * Endpoint ``` GET /rest/api/v1/governance/data/findings/exports ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Lists all DLP findings exports. ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Lists policies * Endpoint ``` GET /rest/api/v1/governance/data/policies ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Lists policies with filtering. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Governance API The Governance API enables administrators to create and manage data governance policies, generate compliance reports, and maintain oversight of content access and usage within your organization. Ensure data security and regulatory compliance through comprehensive governance controls. Configure in the Glean UI under Admin → Governance. You can also configure the same using the APIs provided. Each policy or report scans every indexed document according to its settings and surfaces any violations for review. ## Authorization[​](#authorization "Direct link to Authorization") To call the policy and report endpoints, create a **Client API token** with scope `DATA_GOVERNANCE`. The token must belong to a user who can access the Sensitive Content pages (for example, a Super Admin or Sensitive Content Moderator). For content-hiding endpoints, generate a token with scope `CONTENT_HIDING`. The associated user must be able to edit visibility overrides, typically a Super Admin. See [Authentication](/api-info/client/authentication/overview.md) for detailed steps. ## Difference between policies and reports[​](#difference-between-policies-and-reports "Direct link to Difference between policies and reports") A report runs a one-off scan and lets you download violations as a CSV when it finishes. Policies support two schedules—`WEEKLY` and `CONTINUOUS`. A policy adds an interactive dashboard where you can review findings, archive items, and optionally hide flagged documents. Note: Policy and report APIs share many request fields. Ensure any shared fields use consistent values within the same payload. ## APIs[​](#apis "Direct link to APIs") Governance APIs fall into three groups: **policy**, **report**, and **content-hiding** endpoints. Both policies and reports share a common configuration object, **config**. This has certain fields that need not be populated while submitting a report or policy to be created as they are only meant to be populated on return. Namely, the config **version**, **createdAt** and **createdBy** will automatically be set without needing to be passed. ### Policy APIs[​](#policy-apis "Direct link to Policy APIs") With these endpoints, you can perform CRUD operations on policies. Policies created/edited through these would reflect in the Sensitive Findings section under the Admin > Governance section in the Glean UI. ### Report APIs[​](#report-apis "Direct link to Report APIs") Only creating a one-time report is supported via API for reports. You can get the report's scan status to check if it has completed and download its violation CSV. For any of the other features such as updating, other frequencies, etc., please use policies. Reports created using this endpoint will appear in the "Sensitive Content Reporting" section under Admin > Governance in the UI. ### Content hiding APIs[​](#content-hiding-apis "Direct link to Content hiding APIs") Content hiding works by setting "visibility overrides" to a particular document. These overrides can come from different sources, such as:  1. Existing policies that might flag a document meeting its requirements if "auto-hide" is enabled. 2. If it is manually hidden by a user. Different users are treated as different sources. 3. If a document is present in the CSV for hiding it. On manually hiding, the document will have a visibility override set with source type "user". On manually unhiding, the document will be unhidden from all sources (not just the user source). All its visibility overrides will be removed and the document will be visible on Glean. ## Use Cases ### Policy Management Create, update, and enforce data governance policies to ensure compliance with organizational and regulatory requirements. ### Compliance Reporting Generate detailed compliance reports and audit trails to demonstrate adherence to data governance policies and regulations. ### Access Control Monitor and control data access patterns to ensure sensitive information is only accessed by authorized personnel. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | GET | [`/rest/api/v1/governance/data/findings/exports`](/api/client-api/governance/listfindingsexports.md) | [Lists findings exports](/api/client-api/governance/listfindingsexports.md)Lists all DLP findings exports. | | POST | [`/rest/api/v1/governance/data/findings/exports`](/api/client-api/governance/createfindingsexport.md) | [Creates findings export](/api/client-api/governance/createfindingsexport.md)Creates a new DLP findings export job. | | DELETE | [`/rest/api/v1/governance/data/findings/exports/{id}`](/api/client-api/governance/deletefindingsexport.md) | [Deletes findings export](/api/client-api/governance/deletefindingsexport.md)Deletes a DLP findings export. | | GET | [`/rest/api/v1/governance/data/findings/exports/{id}`](/api/client-api/governance/downloadfindingsexport.md) | [Downloads findings export](/api/client-api/governance/downloadfindingsexport.md)Downloads a DLP findings export as a CSV file. | | GET | [`/rest/api/v1/governance/data/policies`](/api/client-api/governance/listpolicies.md) | [Lists policies](/api/client-api/governance/listpolicies.md)Lists policies with filtering. | | POST | [`/rest/api/v1/governance/data/policies`](/api/client-api/governance/createpolicy.md) | [Creates new policy](/api/client-api/governance/createpolicy.md)Creates a new policy with specified specifications and returns its id. | | GET | [`/rest/api/v1/governance/data/policies/{id}`](/api/client-api/governance/getpolicy.md) | [Gets specified policy](/api/client-api/governance/getpolicy.md)Fetches the specified policy version, or the latest if no version is provided. | | POST | [`/rest/api/v1/governance/data/policies/{id}`](/api/client-api/governance/updatepolicy.md) | [Updates an existing policy](/api/client-api/governance/updatepolicy.md)Updates an existing policy. | | GET | [`/rest/api/v1/governance/data/policies/{id}/download`](/api/client-api/governance/downloadpolicycsv.md) | [Downloads violations CSV for policy](/api/client-api/governance/downloadpolicycsv.md)Downloads CSV violations report for a specific policy id. This does not support continuous policies. | | POST | [`/rest/api/v1/governance/data/reports`](/api/client-api/governance/createreport.md) | [Creates new one-time report](/api/client-api/governance/createreport.md)Creates a new one-time report and executes its batch job. | | GET | [`/rest/api/v1/governance/data/reports/{id}/download`](/api/client-api/governance/downloadreportcsv.md) | [Downloads violations CSV for report](/api/client-api/governance/downloadreportcsv.md)Downloads CSV violations report for a specific report id. | | GET | [`/rest/api/v1/governance/data/reports/{id}/status`](/api/client-api/governance/getreportstatus.md) | [Fetches report run status](/api/client-api/governance/getreportstatus.md)Fetches the status of the run corresponding to the report-id. | | GET | [`/rest/api/v1/governance/documents/visibilityoverrides`](/api/client-api/governance/getdocvisibility.md) | [Fetches documents visibility](/api/client-api/governance/getdocvisibility.md)Fetches the visibility override status of the documents passed. | | POST | [`/rest/api/v1/governance/documents/visibilityoverrides`](/api/client-api/governance/setdocvisibility.md) | [Hide or unhide docs](/api/client-api/governance/setdocvisibility.md)Sets the visibility-override state of the documents specified, effectively hiding or un-hiding documents. | --- # Hide or unhide docs * Endpoint ``` POST /rest/api/v1/governance/documents/visibilityoverrides ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Sets the visibility-override state of the documents specified, effectively hiding or un-hiding documents. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Updates an existing policy * Endpoint ``` POST /rest/api/v1/governance/data/policies/:id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Updates an existing policy. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 403 * 500 OK Permissions error Internal error --- # Get insights * Endpoint ``` POST /rest/api/v1/insights ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) * Deprecations 1 deprecated field · earliest removal January 15, 2027 `searchSessionSatisfaction` This property is no longer supported. Please contact Support for alternatives. Deprecation introduced on 2026-05-13|Will be removed after 2027-01-15 Gets the aggregate usage insights data displayed in the Insights Dashboards. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Insights API The Insights API provides access to aggregated analytics and usage data about users, queries, and content within your Glean instance. Generate reports and dashboards to understand how your organization uses knowledge resources and identify opportunities for improvement. ## Use Cases ### Usage Analytics Analyze user behavior, search patterns, and content engagement to understand how your organization accesses and uses knowledge resources. ### Performance Metrics Track search performance, user satisfaction, and content effectiveness to optimize your knowledge management strategy. ### Business Intelligence Create custom dashboards and reports to provide leadership with insights into knowledge usage and organizational productivity. ## API Endpoints | Method | Endpoint | Purpose | | ------ | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/insights`](/api/client-api/insights/insights.md) | [Get insights](/api/client-api/insights/insights.md)Gets the aggregate usage insights data displayed in the Insights Dashboards. | --- # Read messages * Endpoint ``` POST /rest/api/v1/messages ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Retrieves list of messages from messaging/chat datasources (e.g. Slack, Teams). ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Messages API The Messages API provides access to messages from messaging and chat platforms like Slack and Microsoft Teams. Retrieve conversation data, search across team communications, and integrate messaging insights into your applications. ## Use Cases ### Communication Search Search across team messages and conversations to find important discussions, decisions, and knowledge shared in chat platforms. ### Knowledge Extraction Extract valuable insights and institutional knowledge from team conversations and chat histories across different platforms. ### Communication Analytics Analyze messaging patterns and team communication trends to improve collaboration and knowledge sharing practices. ## API Endpoints | Method | Endpoint | Purpose | | ------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | POST | [`/rest/api/v1/messages`](/api/client-api/messages/messages.md) | [Read messages](/api/client-api/messages/messages.md)Retrieves list of messages from messaging/chat datasources (e.g. Slack, Teams). | --- # Update pin * Endpoint ``` POST /rest/api/v1/editpin ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Update an existing user-generated pin. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Read pin * Endpoint ``` POST /rest/api/v1/getpin ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Read pin details given its ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # List pins * Endpoint ``` POST /rest/api/v1/listpins ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Lists all pins. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Pins API The Pins API allows you to create, manage, and organize pinned search results that appear prominently for specific queries. Improve search experience by ensuring high-quality, relevant content appears at the top of search results for important topics. ## Use Cases ### Search Result Curation Pin high-quality documents and resources to appear at the top of search results for specific queries, ensuring users find the best content first. ### Content Promotion Highlight important announcements, policies, and frequently accessed documents by pinning them to relevant search terms. ### Search Quality Control Manage and update pinned results to maintain search quality and ensure users consistently find the most relevant and current information. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/editpin`](/api/client-api/pins/editpin.md) | [Update pin](/api/client-api/pins/editpin.md)Update an existing user-generated pin. | | POST | [`/rest/api/v1/getpin`](/api/client-api/pins/getpin.md) | [Read pin](/api/client-api/pins/getpin.md)Read pin details given its ID. | | POST | [`/rest/api/v1/listpins`](/api/client-api/pins/listpins.md) | [List pins](/api/client-api/pins/listpins.md)Lists all pins. | | POST | [`/rest/api/v1/pin`](/api/client-api/pins/pin.md) | [Create pin](/api/client-api/pins/pin.md)Pin a document as a result for a given search query.Pin results that are known to be a good match. | | POST | [`/rest/api/v1/unpin`](/api/client-api/pins/unpin.md) | [Delete pin](/api/client-api/pins/unpin.md)Unpin a previously pinned result. | --- # Create pin * Endpoint ``` POST /rest/api/v1/pin ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Pin a document as a result for a given search query.Pin results that are known to be a good match. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Delete pin * Endpoint ``` POST /rest/api/v1/unpin ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Unpin a previously pinned result. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Forbidden from unpinning someone else's pin Too Many Requests --- # Search the index (admin) * Endpoint ``` POST /rest/api/v1/adminsearch ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Retrieves results for search query without respect for permissions. This is available only to privileged users. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 422 * 429 OK Invalid request Not Authorized Forbidden Invalid Query Too Many Requests --- # Autocomplete * Endpoint ``` POST /rest/api/v1/autocomplete ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Retrieve query suggestions, operators and documents for the given partially typed query. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Feed of documents and events * Endpoint ``` POST /rest/api/v1/feed ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) The personalized feed/home includes different types of contents including suggestions, recents, calendar events and many more. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 408 * 429 OK Invalid request Not Authorized Request Timeout Too Many Requests --- # Search API The Search API is the core of Glean's search functionality, enabling you to perform powerful searches across your organization's content. Access search results, autocomplete suggestions, and personalized feeds to build custom search experiences and knowledge discovery applications. ## Use Cases ### Custom Search Interfaces Build tailored search experiences for your applications using Glean's powerful search capabilities with custom filtering and ranking. ### Autocomplete Integration Enhance user experience with intelligent autocomplete suggestions that help users discover relevant content as they type. ### Personalized Content Discovery Leverage personalized feeds and search results to surface the most relevant content for each user based on their role and interests. ## API Endpoints | Method | Endpoint | Purpose | | ------ | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/adminsearch`](/api/client-api/search/adminsearch.md) | [Search the index (admin)](/api/client-api/search/adminsearch.md)Retrieves results for search query without respect for permissions. This is available only to privileged users. | | POST | [`/rest/api/v1/autocomplete`](/api/client-api/search/autocomplete.md) | [Autocomplete](/api/client-api/search/autocomplete.md)Retrieve query suggestions, operators and documents for the given partially typed query. | | POST | [`/rest/api/v1/feed`](/api/client-api/search/feed.md) | [Feed of documents and events](/api/client-api/search/feed.md)The personalized feed/home includes different types of contents including suggestions, recents, calendar events and many more. | | POST | [`/rest/api/v1/recommendations`](/api/client-api/search/recommendations.md) | [Recommend documents](/api/client-api/search/recommendations.md)Retrieve recommended documents for the given URL or Glean Document ID. | | POST | [`/rest/api/v1/search`](/api/client-api/search/search.md) | [Search](/api/client-api/search/search.md)Retrieve results from the index for the given query and filters. | --- # Recommend documents * Endpoint ``` POST /rest/api/v1/recommendations ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Retrieve recommended documents for the given URL or Glean Document ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 202 * 204 * 400 * 401 * 403 * 429 OK Accepted. The Retry-After header has a hint about when the response will be available There are no recommendations for this URL Invalid request Not Authorized Document does not exist or user cannot access document Too Many Requests --- # Search * Endpoint ``` POST /rest/api/v1/search ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Retrieve results from the index for the given query and filters. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 408 * 422 * 429 OK Invalid request Not Authorized Forbidden Request Timeout Invalid Query Too Many Requests --- # Create shortcut * Endpoint ``` POST /rest/api/v1/createshortcut ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Create a user-generated shortcut that contains an alias and destination URL. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Delete shortcut * Endpoint ``` POST /rest/api/v1/deleteshortcut ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Delete an existing user-generated shortcut. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Read shortcut * Endpoint ``` POST /rest/api/v1/getshortcut ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Read a particular shortcut's details given its ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # List shortcuts * Endpoint ``` POST /rest/api/v1/listshortcuts ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) List shortcuts editable/owned by the currently authenticated user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Shortcuts API The Shortcuts API enables you to create and manage search shortcuts that provide quick access to frequently used queries and resources. Build custom shortcuts that help users navigate to important content and perform common searches more efficiently. ## Use Cases ### Quick Access Links Create shortcuts that instantly navigate users to frequently accessed documents, dashboards, and resources without complex searches. ### Saved Searches Enable users to save and quickly re-execute common search queries with predefined filters and parameters. ### Workflow Optimization Streamline user workflows by providing one-click access to routine searches and frequently needed information. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | POST | [`/rest/api/v1/createshortcut`](/api/client-api/shortcuts/createshortcut.md) | [Create shortcut](/api/client-api/shortcuts/createshortcut.md)Create a user-generated shortcut that contains an alias and destination URL. | | POST | [`/rest/api/v1/deleteshortcut`](/api/client-api/shortcuts/deleteshortcut.md) | [Delete shortcut](/api/client-api/shortcuts/deleteshortcut.md)Delete an existing user-generated shortcut. | | POST | [`/rest/api/v1/getshortcut`](/api/client-api/shortcuts/getshortcut.md) | [Read shortcut](/api/client-api/shortcuts/getshortcut.md)Read a particular shortcut's details given its ID. | | POST | [`/rest/api/v1/listshortcuts`](/api/client-api/shortcuts/listshortcuts.md) | [List shortcuts](/api/client-api/shortcuts/listshortcuts.md)List shortcuts editable/owned by the currently authenticated user. | | POST | [`/rest/api/v1/updateshortcut`](/api/client-api/shortcuts/updateshortcut.md) | [Update shortcut](/api/client-api/shortcuts/updateshortcut.md)Updates the shortcut with the given ID. | --- # Update shortcut * Endpoint ``` POST /rest/api/v1/updateshortcut ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Updates the shortcut with the given ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Summarize API The Summarize API leverages AI to generate concise summaries of documents and content within your organization. Extract key insights and create digestible overviews of lengthy documents to help users quickly understand important information. ## Use Cases ### Document Summarization Generate concise summaries of lengthy documents, reports, and articles to help users quickly grasp key points and main ideas. ### Content Digests Create automated content digests and executive summaries for newsletters, reports, and knowledge sharing initiatives. ### Information Extraction Extract and summarize key insights from multiple documents to support research, analysis, and decision-making processes. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/summarize`](/api/client-api/summarize/summarize.md) | [Summarize documents](/api/client-api/summarize/summarize.md)Generate an AI summary of the requested documents. | --- # Summarize documents * Endpoint ``` POST /rest/api/v1/summarize ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Generate an AI summary of the requested documents. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Start the OAuth authorization flow for an action pack. * Endpoint ``` POST /rest/api/v1/actions/actionpack/:actionPackId/auth ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Starts the third-party OAuth flow for the specified action pack and returns the redirect URL that the client should navigate the end user to. After the OAuth callback completes, the user's browser is redirected back to `returnUrl` with a status query parameter (`?glean_action_auth=success|error&actionPackId=...`). `returnUrl` must match the tenant's configured return URL allowlist; otherwise the request is rejected with 400. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 429 Successful operation Invalid request (e.g. returnUrl not in allowlist, unsupported auth type) Unauthorized User not entitled to the action pack Action pack not found Too Many Requests --- # Start the OAuth authorization flow for a tool server. * Endpoint ``` POST /rest/api/v1/tool-servers/:serverId/auth ``` * Release stage ExperimentalIntroduced July 2, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Initiates the third-party OAuth flow for the specified tool server and returns the authorization URL that the client should navigate the end user to. After the OAuth callback completes, the user's browser is redirected back to `returnUrl` with query parameters indicating the result. `returnUrl` must match the tenant's configured return URL allowlist; otherwise the request is rejected with 400. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 429 Successful operation Invalid request (e.g. returnUrl not in allowlist, unsupported auth type) Unauthorized User not entitled to the tool server Tool server not found Too Many Requests --- # Execute the specified tool * Endpoint ``` POST /rest/api/v1/tools/call ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Execute the specified tool with provided parameters ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 Successful operation Bad Request Unauthorized Not Found Too Many Requests --- # Get end-user authentication status for an action pack. * Endpoint ``` GET /rest/api/v1/actions/actionpack/:actionPackId/auth ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Reports whether the calling user is already authenticated against the third-party tool backing the specified action pack. Intended for headless / server-driven clients that render an "Authorize" prompt when the user has not yet consented to the tool. ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 Successful operation Bad Request Unauthorized Action pack not found Too Many Requests --- # Get end-user authentication status for a tool server. * Endpoint ``` GET /rest/api/v1/tool-servers/:serverId/auth ``` * Release stage ExperimentalIntroduced July 2, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Returns display information and the calling user's current authentication status for the specified tool server. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 Successful operation Bad Request Unauthorized Tool server not found Too Many Requests --- # Get tool definitions from a tool server. * Endpoint ``` GET /rest/api/v1/tool-servers/:serverId/tools ``` * Release stage ExperimentalIntroduced July 30, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Returns the name, description and JSON input schema for the named tools on the specified tool server. Works for Glean's built-in tools, tool packs and MCP servers. `toolNames` is required. Names that do not exist on the server are returned in `notFound` rather than failing the request, so a single bad name does not force callers into one-at-a-time retries. Matching is case-insensitive and treats `-` and `_` as equivalent. Use `serverId=native` for Glean's built-in tools. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 * 503 Successful operation Invalid request (e.g. missing toolNames, or more than 100 names) Unauthorized Tool server not found Too Many Requests Tool schemas could not be resolved from the server --- # List available tools * Endpoint ``` GET /rest/api/v1/tools/list ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Returns a filtered set of available tools based on optional tool name parameters. If no filters are provided, all available tools are returned. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 Successful operation Bad Request Unauthorized Not Found Too Many Requests --- # Tools API The Tools API enables you to discover and execute various tools and integrations available within your Glean instance. Access external services, run automated workflows, and integrate with third-party applications to extend Glean's capabilities. ## Use Cases ### Tool Discovery Discover available tools and integrations within your Glean instance to understand what capabilities are accessible through the API. ### Workflow Automation Execute tools programmatically to automate workflows, data processing, and integration tasks with external systems. ### Third-party Integration Integrate with external services and applications through Glean's tool ecosystem to create comprehensive business solutions. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | GET | [`/rest/api/v1/actions/actionpack/{actionPackId}/auth`](/api/client-api/tools/get-action-pack-auth-status.md) | [Get end-user authentication status for an action pack.](/api/client-api/tools/get-action-pack-auth-status.md)Reports whether the calling user is already authenticated against the third-party tool backing the specified action pack. Intended for headless / server-driven clients that render an "Authorize" prompt when the user has not yet consented to the tool. | | POST | [`/rest/api/v1/actions/actionpack/{actionPackId}/auth`](/api/client-api/tools/authorize-action-pack.md) | [Start the OAuth authorization flow for an action pack.](/api/client-api/tools/authorize-action-pack.md)Starts the third-party OAuth flow for the specified action pack and returns the redirect URL that the client should navigate the end user to. After the OAuth callback completes, the user's browser is redirected back to \`returnUrl\` with a status query parameter (\`?glean\_action\_auth=success\|error\&actionPackId=...\`). \`returnUrl\` must match the tenant's configured return URL allowlist; otherwise the request is rejected with 400. | | GET | [`/rest/api/v1/tool-servers/{serverId}/auth`](/api/client-api/tools/get-tool-server-auth-status.md) | [Get end-user authentication status for a tool server.](/api/client-api/tools/get-tool-server-auth-status.md)Returns display information and the calling user's current authentication status for the specified tool server. | | POST | [`/rest/api/v1/tool-servers/{serverId}/auth`](/api/client-api/tools/authorize-tool-server.md) | [Start the OAuth authorization flow for a tool server.](/api/client-api/tools/authorize-tool-server.md)Initiates the third-party OAuth flow for the specified tool server and returns the authorization URL that the client should navigate the end user to. After the OAuth callback completes, the user's browser is redirected back to \`returnUrl\` with query parameters indicating the result. \`returnUrl\` must match the tenant's configured return URL allowlist; otherwise the request is rejected with 400. | | GET | [`/rest/api/v1/tool-servers/{serverId}/tools`](/api/client-api/tools/get-tool-server-tools.md) | [Get tool definitions from a tool server.](/api/client-api/tools/get-tool-server-tools.md)Returns the name, description and JSON input schema for the named tools on the specified tool server. Works for Glean's built-in tools, tool packs and MCP servers. \`toolNames\` is required. Names that do not exist on the server are returned in \`notFound\` rather than failing the request, so a single bad name does not force callers into one-at-a-time retries. Matching is case-insensitive and treats \`-\` and \`\_\` as equivalent. Use \`serverId=native\` for Glean's built-in tools. | | POST | [`/rest/api/v1/tools/call`](/api/client-api/tools/execute-the-specified-tool.md) | [Execute the specified tool](/api/client-api/tools/execute-the-specified-tool.md)Execute the specified tool with provided parameters | | GET | [`/rest/api/v1/tools/list`](/api/client-api/tools/list-available-tools.md) | [List available tools](/api/client-api/tools/list-available-tools.md)Returns a filtered set of available tools based on optional tool name parameters. If no filters are provided, all available tools are returned. | --- # Create verification * Endpoint ``` POST /rest/api/v1/addverificationreminder ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Creates a verification reminder for the document. Users can create verification reminders from different product surfaces. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Document does not exist, does not support verification or user cannot access document Too Many Requests --- # List verifications * Endpoint ``` POST /rest/api/v1/listverifications ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Returns the information to be rendered in verification dashboard. Includes information for each document owned by user regarding their verifications. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Invalid request Not Authorized Too Many Requests --- # Verification API The Verification API helps maintain content accuracy and currency by managing verification workflows for documents and information. Create verification reminders, track verification status, and ensure your organization's knowledge remains up-to-date and reliable. ## Use Cases ### Content Verification Set up automated reminders and workflows to ensure documents and information are regularly reviewed and verified for accuracy. ### Quality Assurance Track verification status across your content to maintain high-quality, reliable information throughout your organization. ### Compliance Tracking Monitor verification activities and maintain audit trails to demonstrate compliance with content governance policies. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/rest/api/v1/addverificationreminder`](/api/client-api/verification/addverificationreminder.md) | [Create verification](/api/client-api/verification/addverificationreminder.md)Creates a verification reminder for the document. Users can create verification reminders from different product surfaces. | | POST | [`/rest/api/v1/listverifications`](/api/client-api/verification/listverifications.md) | [List verifications](/api/client-api/verification/listverifications.md)Returns the information to be rendered in verification dashboard. Includes information for each document owned by user regarding their verifications. | | POST | [`/rest/api/v1/verify`](/api/client-api/verification/verify.md) | [Update verification](/api/client-api/verification/verify.md)Verify documents to keep the knowledge up to date within customer corpus. | --- # Update verification * Endpoint ``` POST /rest/api/v1/verify ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Verify documents to keep the knowledge up to date within customer corpus. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 429 OK Invalid request Not Authorized Document does not exist, does not support verification or user cannot access document Too Many Requests --- # Indexing API The Indexing API makes content from internal tools, on-premises systems, and custom applications searchable in Glean. Use it to manage datasources, index content, apply permissions, and keep search results current. ## Start here[​](#start-here "Direct link to Start here") ### [Quickstart](/api-info/indexing/getting-started/overview.md) [Create a datasource and index your first document.](/api-info/indexing/getting-started/overview.md) ### [Authentication](/api-info/indexing/authentication/overview.md) [Create and configure an Indexing API token.](/api-info/indexing/authentication/overview.md) ### [API Reference](#indexing-api-reference) [Browse Indexing API endpoints by capability.](#indexing-api-reference) ## Indexing workflow[​](#indexing-workflow "Direct link to Indexing workflow") 1 Create a datasource Set up a datasource to organize the content you want Glean to discover. Follow the [datasource setup guide](/api-info/indexing/getting-started/setup-datasource.md). 2 Index content Index content with the [Documents](/api/indexing-api/documents-overview.md), [People](/api/indexing-api/people-overview.md), and [Permissions](/api/indexing-api/permissions-overview.md) references. 3 Apply permissions Index permissions and validate document access so users only see content they are authorized to view. 4 Validate and troubleshoot Use the [Indexing API Quickstart](/api-info/indexing/getting-started/overview.md) and the troubleshooting guides in the sidebar to verify your datasource and indexed content. ## Indexing API reference[​](#indexing-api-reference "Direct link to Indexing API reference") * [AuthenticationManage the authentication tokens used to authorize Indexing API requests.1](/api/indexing-api/authentication-overview.md) [ ](/api/indexing-api/authentication-overview.md) [endpoint](/api/indexing-api/authentication-overview.md) * [Custom MetadataAttach custom metadata to indexed documents and manage metadata schemas.5](/api/indexing-api/add-or-update-custom-metadata.md) [ ](/api/indexing-api/add-or-update-custom-metadata.md) [endpoints](/api/indexing-api/add-or-update-custom-metadata.md) * [DatasourcesCreate, configure, and manage the datasources that hold your content.3](/api/indexing-api/datasources-overview.md) [ ](/api/indexing-api/datasources-overview.md) [endpoints](/api/indexing-api/datasources-overview.md) * [DocumentsIndex documents individually or in bulk, then update permissions and remove stale content.6](/api/indexing-api/documents-overview.md) [ ](/api/indexing-api/documents-overview.md) [endpoints](/api/indexing-api/documents-overview.md) * [PeopleIndex employees and teams used by Glean's people directory and retrieval experiences.7](/api/indexing-api/people-overview.md) [ ](/api/indexing-api/people-overview.md) [endpoints](/api/indexing-api/people-overview.md) * [PermissionsIndex users, groups, and memberships that control access to indexed content.11](/api/indexing-api/permissions-overview.md) [ ](/api/indexing-api/permissions-overview.md) [endpoints](/api/indexing-api/permissions-overview.md) * [ShortcutsCreate and manage search shortcuts for frequently used queries, tools, and resources.2](/api/indexing-api/shortcuts-overview.md) [ ](/api/indexing-api/shortcuts-overview.md) [endpoints](/api/indexing-api/shortcuts-overview.md) * [TroubleshootingDebug indexing operations, verify data integrity, and monitor processing status.9](/api/indexing-api/troubleshooting-overview.md) [ ](/api/indexing-api/troubleshooting-overview.md) [endpoints](/api/indexing-api/troubleshooting-overview.md) ## Cross-datasource enrichment[​](#cross-datasource-enrichment "Direct link to Cross-datasource enrichment") [Custom Metadata](/api-info/indexing/custom-metadata/overview.md) is grouped with Indexing API guidance because it enriches indexed content and uses Glean-issued credentials, but it uses the distinct `/rest/api/index/custom-metadata/...` base path. It can attach metadata to documents from any datasource, including native connectors, without re-indexing the source document. --- # Add or update custom metadata * Endpoint ``` PUT /rest/api/index/document/:docId/custom-metadata/:groupName ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Associates custom metadata with a specific document. Custom metadata enables you to enrich documents with additional structured information that can be used for search, filtering, and faceting. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 * 500 OK Bad Request Not Authorized Not Found Too Many Requests Internal Server Error --- # Add or update datasource * Endpoint ``` POST /api/index/v1/adddatasource ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Add or update a custom datasource and its schema. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 OK Bad Request Not Authorized --- # Overview Manage authentication tokens for the Indexing API. The Indexing API requires authentication tokens to authorize access to your Glean instance. ## Use Cases ### Token Rotation Implement secure token rotation policies to maintain API security and compliance with your organization's security requirements. ### Multi-Environment Setup Manage separate authentication tokens for development, staging, and production environments to ensure proper access control. ### Service Integration Authenticate third-party services and custom applications that need to index content into your Glean instance. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ---------------------------------------------------------------- | ------------------------------------------------- | | POST | [`/api/index/v1/rotatetoken`](/api/indexing-api/rotate-token.md) | [Rotate token](/api/indexing-api/rotate-token.md) | --- # Beta: Get datasource status * Endpoint ``` POST /api/index/v1/debug/:datasource/status ``` * Release stage Beta This endpoint is in Beta. Expect changes and instability. * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Gather information about the datasource's overall status. Currently in beta, might undergo breaking changes without prior notice. Tip: Refer to the [Troubleshooting tutorial](https://developers.glean.com/indexing/debugging/datasource-config) for more information. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 OK Bad Request Not Authorized --- # Beta: Get document information * Endpoint ``` POST /api/index/v1/debug/:datasource/document ``` * Release stage Beta This endpoint is in Beta. Expect changes and instability. * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Gives various information that would help in debugging related to a particular document. Currently in beta, might undergo breaking changes without prior notice. Tip: Refer to the [Troubleshooting tutorial](https://developers.glean.com/indexing/debugging/datasource-config) for more information. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 OK Bad Request Not Authorized --- # Beta: Get document lifecycle events * Endpoint ``` POST /api/index/v1/debug/:datasource/document/events ``` * Release stage Beta This endpoint is in Beta. Expect changes and instability. * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Retrieves lifecycle events for a specific document including upload time, index times and deletions. Rate limited to 1 request per minute per datasource. Currently in beta, might undergo breaking changes without prior notice. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Bad Request Not Authorized Too Many Requests --- # Beta: Get information of a batch of documents * Endpoint ``` POST /api/index/v1/debug/:datasource/documents ``` * Release stage Beta This endpoint is in Beta. Expect changes and instability. * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Gives various information that would help in debugging related to a batch of documents. Currently in beta, might undergo breaking changes without prior notice. Tip: Refer to the [Troubleshooting tutorial](https://developers.glean.com/indexing/debugging/datasource-config) for more information. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 OK Bad Request Not Authorized --- # Beta: Get user information * Endpoint ``` POST /api/index/v1/debug/:datasource/user ``` * Release stage Beta This endpoint is in Beta. Expect changes and instability. * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Gives various information that would help in debugging related to a particular user. Currently in beta, might undergo breaking changes without prior notice. Tip: Refer to the [Troubleshooting tutorial](https://developers.glean.com/indexing/debugging/datasource-config) for more information. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 OK Bad Request Not Authorized --- # Beta users * Endpoint ``` POST /api/index/v1/betausers ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Allow the datasource be visible to the specified beta users. The default behaviour is datasource being visible to all users if it is enabled and not visible to any user if it is not enabled. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Bulk index documents * Endpoint ``` POST /api/index/v1/bulkindexdocuments ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Replaces the documents in a datasource using paginated batch API calls. Please refer to the [bulk indexing](https://developers.glean.com/indexing/documents/bulk-upload-model) documentation for an explanation of how to use bulk endpoints. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Bulk index employees * Endpoint ``` POST /api/index/v1/bulkindexemployees ``` * Release stage DeprecatedSince February 3, 2026 · Removal on January 15, 2027 · [All deprecations](/deprecations/.md) * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Replaces all the currently indexed employees using paginated batch API calls. Please refer to the [bulk indexing](https://developers.glean.com/indexing/documents/bulk-upload-model) documentation for an explanation of how to use bulk endpoints. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Bulk index external shortcuts * Endpoint ``` POST /api/index/v1/bulkindexshortcuts ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Replaces all the currently indexed shortcuts using paginated batch API calls. Note that this endpoint is used for indexing shortcuts not hosted by Glean. If you want to upload shortcuts that would be hosted by Glean, please use the `/uploadshortcuts` endpoint. For information on what you can do with Golinks, which are Glean-hosted shortcuts, please refer to [this](https://docs.glean.com/user-guide/knowledge/go-links/how-go-links-work) page. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Bulk index groups * Endpoint ``` POST /api/index/v1/bulkindexgroups ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Replaces the groups in a datasource using paginated batch API calls. Please refer to the [bulk indexing](https://developers.glean.com/indexing/documents/bulk-upload-model) documentation for an explanation of how to use bulk endpoints. Note: Any groups deleted from the existing set will have their associated memberships deleted as well. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Bulk index memberships for a group * Endpoint ``` POST /api/index/v1/bulkindexmemberships ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Replaces the memberships for a group in a datasource using paginated batch API calls. Please refer to the [bulk indexing](https://developers.glean.com/indexing/documents/bulk-upload-model) documentation for an explanation of how to use bulk endpoints. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Bulk index teams * Endpoint ``` POST /api/index/v1/bulkindexteams ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Replaces all the currently indexed teams using paginated batch API calls. Please refer to the [bulk indexing](https://developers.glean.com/indexing/documents/bulk-upload-model) documentation for an explanation of how to use bulk endpoints. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Bulk index users * Endpoint ``` POST /api/index/v1/bulkindexusers ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Replaces the users in a datasource using paginated batch API calls. Please refer to the [bulk indexing](https://developers.glean.com/indexing/documents/bulk-upload-model) documentation for an explanation of how to use bulk endpoints. Note: Any users deleted from the existing set will have their associated memberships deleted as well. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Check document access * Endpoint ``` POST /api/index/v1/checkdocumentaccess ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Check if a given user has access to access a document in a custom datasource Tip: Refer to the [Troubleshooting tutorial](https://developers.glean.com/indexing/debugging/datasource-config) for more information. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Bad Request Not Authorized Too Many Requests --- # Create or update metadata schema * Endpoint ``` PUT /rest/api/index/custom-metadata/schema/:groupName ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Defines or updates the schema for a metadata group. Schemas should be defined before indexing metadata. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 * 429 * 500 OK Bad Request Not Authorized Conflict - Schema already exists with incompatible changes Too Many Requests Internal Server Error --- # Overview Configure and manage datasources for indexing content. Datasources define the origin and configuration settings for content that will be indexed into Glean. ## Use Cases ### Custom Datasource Setup Create and configure custom datasources for proprietary systems, internal tools, or specialized content repositories that need to be searchable in Glean. ### Configuration Management Update datasource configurations to modify indexing behavior, content processing rules, and metadata extraction settings. ### Integration Monitoring Retrieve datasource configuration details to troubleshoot indexing issues and verify integration settings. ## API Endpoints | Method | Endpoint | Purpose | | ------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | POST | [`/api/index/v1/adddatasource`](/api/indexing-api/add-or-update-datasource.md) | [Add or update datasource](/api/indexing-api/add-or-update-datasource.md) | | POST | [`/api/index/v1/getdatasourceconfig`](/api/indexing-api/get-datasource-config.md) | [Get datasource config](/api/indexing-api/get-datasource-config.md) | --- # Delete document * Endpoint ``` POST /api/index/v1/deletedocument ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Deletes the specified document from the index. Succeeds if document is not present. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Delete employee * Endpoint ``` POST /api/index/v1/deleteemployee ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Delete an employee. Silently succeeds if employee is not present. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Delete group * Endpoint ``` POST /api/index/v1/deletegroup ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Delete group from the datasource. Silently succeeds if group is not present. Note: All memberships associated with the deleted group will also be deleted. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Delete membership * Endpoint ``` POST /api/index/v1/deletemembership ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Delete membership to a group in the specified datasource. Silently succeeds if membership is not present. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Delete team * Endpoint ``` POST /api/index/v1/deleteteam ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Delete a team based on provided id. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Delete user * Endpoint ``` POST /api/index/v1/deleteuser ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Delete the user from the datasource. Silently succeeds if user is not present. Note: All memberships associated with the deleted user will also be deleted. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Overview Index, update, and manage documents in your Glean instance. These endpoints handle the core document indexing workflow, from individual documents to bulk operations. ## Use Cases ### Real-time Content Indexing Index documents immediately as they are created or updated in your systems to ensure search results are always current. ### Bulk Content Migration Efficiently index large volumes of existing content when migrating to Glean or performing initial data loads. ### Permission Management Update document permissions to ensure proper access control and security compliance as organizational structures change. ### Content Lifecycle Management Remove outdated or deleted documents from the search index to maintain result quality and data accuracy. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | | POST | [`/api/index/v1/indexdocument`](/api/indexing-api/index-document.md) | [Index document](/api/indexing-api/index-document.md) | | POST | [`/api/index/v1/indexdocuments`](/api/indexing-api/index-documents.md) | [Index documents](/api/indexing-api/index-documents.md) | | POST | [`/api/index/v1/bulkindexdocuments`](/api/indexing-api/bulk-index-documents.md) | [Bulk index documents](/api/indexing-api/bulk-index-documents.md) | | POST | [`/api/index/v1/updatedocumentpermissions`](/api/indexing-api/update-document-permissions.md) | [Update document permissions](/api/indexing-api/update-document-permissions.md) | | POST | [`/api/index/v1/uploaddocuments`](/api/indexing-api/schedules-the-processing-of-uploaded-documents.md) | [Schedules the processing of uploaded documents](/api/indexing-api/schedules-the-processing-of-uploaded-documents.md) | | POST | [`/api/index/v1/deletedocument`](/api/indexing-api/delete-document.md) | [Delete document](/api/indexing-api/delete-document.md) | --- # Get datasource config * Endpoint ``` POST /api/index/v1/getdatasourceconfig ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Fetches the datasource config for the specified custom datasource. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Get document count * Endpoint ``` POST /api/index/v1/getdocumentcount ``` * Release stage DeprecatedSince February 3, 2026 · Removal on October 15, 2026 · [All deprecations](/deprecations/.md) * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Fetches document count for the specified custom datasource. Tip: Use [/debug/{datasource}/status](https://developers.glean.com/indexing/debugging/datasource-status) for richer information. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Get document upload and indexing status * Endpoint ``` POST /api/index/v1/getdocumentstatus ``` * Release stage DeprecatedSince February 3, 2026 · Removal on October 15, 2026 · [All deprecations](/deprecations/.md) * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Intended for debugging/validation. Fetches the current upload and indexing status of documents. Tip: Use [/debug/{datasource}/document](https://developers.glean.com/indexing/debugging/datasource-document) for richer information. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Bad Request Not Authorized Too Many Requests --- # Get user count * Endpoint ``` POST /api/index/v1/getusercount ``` * Release stage DeprecatedSince February 3, 2026 · Removal on October 15, 2026 · [All deprecations](/deprecations/.md) * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Fetches user count for the specified custom datasource. Tip: Use [/debug/{datasource}/status](https://developers.glean.com/indexing/debugging/datasource-status) for richer information. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Index document * Endpoint ``` POST /api/index/v1/indexdocument ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Adds a document to the index or updates an existing document. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 * 429 OK Bad Request Not Authorized Conflict Too Many Requests --- # Index documents * Endpoint ``` POST /api/index/v1/indexdocuments ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Adds or updates multiple documents in the index. Please refer to the [bulk indexing](https://developers.glean.com/indexing/documents/bulk-indexing/choosing-indexdocuments-vs-bulkindexdocuments) documentation for an explanation of when to use this endpoint. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 * 429 OK Bad Request Not Authorized Conflict Too Many Requests --- # Index employee * Endpoint ``` POST /api/index/v1/indexemployee ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) * Deprecations 1 deprecated field · earliest removal October 15, 2026 `location` Field is deprecated Deprecation introduced on 2026-02-05|Will be removed after 2026-10-15 Adds an employee or replaces the existing information about an employee. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Index group * Endpoint ``` POST /api/index/v1/indexgroup ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Add or update a group in the datasource. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Index membership * Endpoint ``` POST /api/index/v1/indexmembership ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Add the memberships of a group in the datasource. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Index team * Endpoint ``` POST /api/index/v1/indexteam ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Adds a team or updates information about a team ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Index user * Endpoint ``` POST /api/index/v1/indexuser ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Adds a datasource user or updates an existing user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Overview Manage employee and team information in your Glean instance. These endpoints handle indexing organizational data to enable people search and proper content attribution. ## Use Cases ### Employee Directory Sync Synchronize employee information from HR systems to maintain accurate people profiles and organizational charts in Glean. ### Team Structure Management Index team hierarchies and organizational structures to enable team-based search and content organization. ### Bulk Organization Updates Efficiently update large numbers of employee records during organizational changes, mergers, or system migrations. ### Onboarding & Offboarding Add new employees and remove departing team members to maintain current organizational data and access controls. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | POST | [`/api/index/v1/indexemployee`](/api/indexing-api/index-employee.md) | [Index employee](/api/indexing-api/index-employee.md) | | POST | [`/api/index/v1/bulkindexemployees`](/api/indexing-api/bulk-index-employees.md) | [Bulk index employees](/api/indexing-api/bulk-index-employees.md) | | POST | [`/api/index/v1/uploademployees`](/api/indexing-api/schedules-the-processing-of-uploaded-employees-and-teams.md) | [Schedules the processing of uploaded employees and teams](/api/indexing-api/schedules-the-processing-of-uploaded-employees-and-teams.md) | | POST | [`/api/index/v1/deleteemployee`](/api/indexing-api/delete-employee.md) | [Delete employee](/api/indexing-api/delete-employee.md) | | POST | [`/api/index/v1/indexteam`](/api/indexing-api/index-team.md) | [Index team](/api/indexing-api/index-team.md) | | POST | [`/api/index/v1/deleteteam`](/api/indexing-api/delete-team.md) | [Delete team](/api/indexing-api/delete-team.md) | | POST | [`/api/index/v1/bulkindexteams`](/api/indexing-api/bulk-index-teams.md) | [Bulk index teams](/api/indexing-api/bulk-index-teams.md) | --- # Overview Manage user access controls and group memberships that determine content visibility in search results. These endpoints ensure users only see content they have permission to access. ## Use Cases ### Access Control Management Define and update user permissions to ensure secure access to sensitive documents and maintain compliance with data governance policies. ### Group-Based Permissions Manage group memberships and permissions to efficiently control access for teams, departments, or project-based groups. ### Dynamic Permission Updates Automatically update user access as roles change, projects evolve, or organizational structures are modified. ### Compliance & Auditing Maintain detailed permission records for compliance requirements and enable auditing of content access controls. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | POST | [`/api/index/v1/indexuser`](/api/indexing-api/index-user.md) | [Index user](/api/indexing-api/index-user.md) | | POST | [`/api/index/v1/bulkindexusers`](/api/indexing-api/bulk-index-users.md) | [Bulk index users](/api/indexing-api/bulk-index-users.md) | | POST | [`/api/index/v1/indexgroup`](/api/indexing-api/index-group.md) | [Index group](/api/indexing-api/index-group.md) | | POST | [`/api/index/v1/bulkindexgroups`](/api/indexing-api/bulk-index-groups.md) | [Bulk index groups](/api/indexing-api/bulk-index-groups.md) | | POST | [`/api/index/v1/indexmembership`](/api/indexing-api/index-membership.md) | [Index membership](/api/indexing-api/index-membership.md) | | POST | [`/api/index/v1/bulkindexmemberships`](/api/indexing-api/bulk-index-memberships-for-a-group.md) | [Bulk index memberships for a group](/api/indexing-api/bulk-index-memberships-for-a-group.md) | | POST | [`/api/index/v1/uploadmemberships`](/api/indexing-api/schedules-the-processing-of-group-memberships.md) | [Schedules the processing of group memberships](/api/indexing-api/schedules-the-processing-of-group-memberships.md) | | POST | [`/api/index/v1/deleteuser`](/api/indexing-api/delete-user.md) | [Delete user](/api/indexing-api/delete-user.md) | | POST | [`/api/index/v1/deletegroup`](/api/indexing-api/delete-group.md) | [Delete group](/api/indexing-api/delete-group.md) | | POST | [`/api/index/v1/deletemembership`](/api/indexing-api/delete-membership.md) | [Delete membership](/api/indexing-api/delete-membership.md) | | POST | [`/api/index/v1/betausers`](/api/indexing-api/beta-users.md) | [Beta users](/api/indexing-api/beta-users.md) | --- # Remove custom metadata * Endpoint ``` DELETE /rest/api/index/document/:docId/custom-metadata/:groupName ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Removes all custom metadata for the specified metadata group from a document. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 * 500 OK Bad Request Not Authorized Not Found Too Many Requests Internal Server Error --- # Remove metadata schema * Endpoint ``` DELETE /rest/api/index/custom-metadata/schema/:groupName ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Deletes the schema definition for a metadata group. This does not delete existing metadata values on documents. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 404 * 429 * 500 OK Bad Request Not Authorized Not Found Too Many Requests Internal Server Error --- # Retrieve metadata schema * Endpoint ``` GET /rest/api/index/custom-metadata/schema/:groupName ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Retrieves the current schema definition for a metadata group. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 401 * 404 * 429 * 500 OK Not Authorized Not Found Too Many Requests Internal Server Error --- # Rotate token * Endpoint ``` POST /api/index/v1/rotatetoken ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Rotates the secret value inside the Indexing API token and returns the new raw secret. All other properties of the token are unchanged. In order to rotate the secret value, include the token as the bearer token in the `/rotatetoken` request. Please refer to [Token rotation](https://developers.glean.com/indexing/authentication/token-rotation) documentation for more information. ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 OK Bad Request Not Authorized --- # Schedules the processing of group memberships * Endpoint ``` POST /api/index/v1/processallmemberships ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Schedules the immediate processing of all group memberships uploaded through the indexing API. By default the uploaded group memberships will be processed asynchronously but this API can be used to schedule processing of all memberships on demand. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 OK Bad Request Not Authorized --- # Schedules the processing of uploaded documents * Endpoint ``` POST /api/index/v1/processalldocuments ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Schedules the immediate processing of documents uploaded through the indexing API. By default the uploaded documents will be processed asynchronously but this API can be used to schedule processing of all documents on demand. If a `datasource` parameter is specified, processing is limited to that custom datasource. Without it, processing applies to all documents across all custom datasources. #### Rate Limits[​](#rate-limits "Direct link to Rate Limits") This endpoint is rate-limited to one usage every 3 hours. Exceeding this limit results in a 429 response code. Here's how the rate limit works: 1. Calling `/processalldocuments` for datasource `foo` prevents another call for `foo` for 3 hours. 2. Calling `/processalldocuments` for datasource `foo` doesn't affect immediate calls for `bar`. 3. Calling `/processalldocuments` for all datasources prevents any datasource calls for 3 hours. 4. Calling `/processalldocuments` for datasource `foo` doesn't affect immediate calls for all datasources. For more frequent document processing, contact Glean support. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Bad Request Not Authorized Too Many Requests --- # Schedules the processing of uploaded employees and teams * Endpoint ``` POST /api/index/v1/processallemployeesandteams ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Schedules the immediate processing of employees and teams uploaded through the indexing API. By default all uploaded people data will be processed asynchronously but this API can be used to schedule its processing on demand. ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 429 OK Bad Request Not Authorized Too Many Requests --- # Overview Create and manage search shortcuts that provide quick access to frequently used queries, tools, and external resources. Shortcuts enhance user productivity by reducing search friction. ## Use Cases ### Quick Access Tools Create shortcuts to commonly used internal tools, dashboards, and applications so users can access them directly from search. ### External Resource Integration Index shortcuts to external resources, documentation sites, and third-party tools that are relevant to your organization. ### Workflow Optimization Bulk upload shortcuts that streamline common workflows and help users discover relevant tools and resources more efficiently. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | POST | [`/api/index/v1/bulkindexshortcuts`](/api/indexing-api/bulk-index-external-shortcuts.md) | [Bulk index external shortcuts](/api/indexing-api/bulk-index-external-shortcuts.md) | | POST | [`/api/index/v1/uploadshortcuts`](/api/indexing-api/upload-shortcuts.md) | [Upload shortcuts](/api/indexing-api/upload-shortcuts.md) | --- # Submit datasource data * Endpoint ``` POST /rest/api/index/submissions/:datasourceInstance/:type ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api-info/client/authentication/overview.md) Validates and asynchronously processes a datasource-specific submission. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 202 * 400 * 401 * 404 * 405 * 413 * 500 * 503 Accepted Bad Request Not Authorized Not Found Method Not Allowed Payload Too Large Internal Server Error Service Unavailable --- # Overview Debug and monitor your indexing operations with diagnostic endpoints. These tools help identify issues, verify data integrity, and ensure successful content indexing. ## Use Cases ### Indexing Status Monitoring Monitor the status of document uploads and indexing operations to ensure content is being processed successfully. ### Data Integrity Verification Verify that documents and user information have been indexed correctly and are accessible through search. ### Access Control Testing Test document access permissions to ensure users can see the content they should have access to. ### Performance Diagnostics Analyze indexing performance and identify bottlenecks in your content processing pipeline. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | POST | [`/api/index/v1/getdatasourcestatus`](/api/indexing-api/beta-get-datasource-status.md) | [Beta: Get datasource status](/api/indexing-api/beta-get-datasource-status.md) | | POST | [`/api/index/v1/getdocumentinfo`](/api/indexing-api/beta-get-document-information.md) | [Beta: Get document information](/api/indexing-api/beta-get-document-information.md) | | POST | [`/api/index/v1/getdocumentsinfo`](/api/indexing-api/beta-get-information-of-a-batch-of-documents.md) | [Beta: Get information of a batch of documents](/api/indexing-api/beta-get-information-of-a-batch-of-documents.md) | | POST | [`/api/index/v1/getuserinfo`](/api/indexing-api/beta-get-user-information.md) | [Beta: Get user information](/api/indexing-api/beta-get-user-information.md) | | POST | [`/api/index/v1/checkdocumentaccess`](/api/indexing-api/check-document-access.md) | [Check document access](/api/indexing-api/check-document-access.md) | | POST | [`/api/index/v1/getdocumentuploadstatus`](/api/indexing-api/get-document-upload-and-indexing-status.md) | [Get document upload and indexing status](/api/indexing-api/get-document-upload-and-indexing-status.md) | | POST | [`/api/index/v1/getdocumentcount`](/api/indexing-api/get-document-count.md) | [Get document count](/api/indexing-api/get-document-count.md) | | POST | [`/api/index/v1/getusercount`](/api/indexing-api/get-user-count.md) | [Get user count](/api/indexing-api/get-user-count.md) | --- # Update document permissions * Endpoint ``` POST /api/index/v1/updatepermissions ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Updates the permissions for a given document without modifying document content. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 * 429 OK Bad Request Not Authorized Conflict Too Many Requests --- # Upload shortcuts * Endpoint ``` POST /api/index/v1/uploadshortcuts ``` * Release stage Generally available * Authorization `Authorization: Bearer `Glean-issued indexing token · [Authentication](/api-info/indexing/authentication/overview.md) Creates glean shortcuts for uploaded shortcuts info. Glean would host the shortcuts, and they can be managed in the knowledge tab once uploaded. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 409 OK Bad Request Not Authorized Conflict --- # Platform API The Platform API is Glean's recommended API for new application integrations. Build modern search, agent, skills, and chat experiences over your organization's connected content. Recommended for new integrations Start with Platform API whenever the capability you need is available and its stability level fits your use case. Review the applicable stability notices before adopting a capability or endpoint. Use [Client API](/api/client-api.md) for existing integrations or capabilities that have not yet moved to Platform API. ## Start here[​](#start-here "Direct link to Start here") ### [Quickstart](/api/platform-api/getting-started.md) [Make your first Platform API request.](/api/platform-api/getting-started.md) ### [Authentication](/api/platform-api/authentication.md) [Choose and configure your Platform API credentials.](/api/platform-api/authentication.md) ### [API Reference](#platform-api-reference) [Browse Platform API endpoints by capability.](#platform-api-reference) ## Platform API reference[​](#platform-api-reference "Direct link to Platform API reference") * [AgentsDiscover available agents, inspect their schemas, and create agent runs.7](/api/platform-api/agents-overview.md) [ ](/api/platform-api/agents-overview.md) [endpoints](/api/platform-api/agents-overview.md) * [ChatCreate chat responses grounded in your Glean instance.2](/api/platform-api/chat-overview.md) [ ](/api/platform-api/chat-overview.md) [endpoints](/api/platform-api/chat-overview.md) * [SearchBuild permission-aware search experiences with relevant results, filters, and time ranges.2](/api/platform-api/search-overview.md) [ ](/api/platform-api/search-overview.md) [endpoints](/api/platform-api/search-overview.md) * [SkillsDiscover, validate, import, synchronize, and manage skills through the Platform API.15](/api/platform-api/skills-overview.md) [ ](/api/platform-api/skills-overview.md) [endpoints](/api/platform-api/skills-overview.md) * [TriggersSubscribe to Glean content events and receive signed webhooks using curated trigger presets.10](/api/platform-api/triggers-overview.md) [ ](/api/platform-api/triggers-overview.md) [endpoints](/api/platform-api/triggers-overview.md) ## Operational resources[​](#operational-resources "Direct link to Operational resources") ### [Errors](/errors/.md) [Look up stable Platform API error codes and response guidance.](/errors/.md) ### [OpenAPI Specification](https://developers.glean.com/oas/platform) [Download the complete Platform API specification.](https://developers.glean.com/oas/platform) --- # Agents API The Agents API lets you discover Glean agents, inspect their input and output schemas, and execute agent runs. Use it to embed Glean's agentic capabilities directly into your own applications and workflows. ## Use Cases ### Agent Discovery Search for agents available to the authenticated user and retrieve their configuration to decide which agent to invoke. ### Schema Inspection Fetch an agent’s input and output schemas so you can build type-safe integrations and validate payloads before invoking a run. ### Agent Execution Create agent runs to execute Glean agents programmatically and incorporate their results into downstream automation. ## API Endpoints | Method | Endpoint | Purpose | | ------ | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | | POST | [`/api/agents/search`](/api/platform-api/platform-agents-search.md) | [Search agents](/api/platform-api/platform-agents-search.md) | | GET | [`/api/agents/{agent_id}`](/api/platform-api/platform-agents-get.md) | [Get agent](/api/platform-api/platform-agents-get.md) | | GET | [`/api/agents/{agent_id}/schemas`](/api/platform-api/platform-agents-get-schemas.md) | [Get agent schemas](/api/platform-api/platform-agents-get-schemas.md) | | POST | [`/api/agents/{agent_id}/runs`](/api/platform-api/platform-agents-create-run.md) | [Create agent run](/api/platform-api/platform-agents-create-run.md) | For per-user integrations, authenticate with OAuth. The [Platform API authentication guide](/api/platform-api/authentication.md) covers the Glean OAuth Authorization Server and external-IdP options. Use a Glean-issued token for global permissions with `X-Glean-ActAs`, or when no OAuth path exists. For error responses, see [Platform API Errors](/errors/.md) for the shared ProblemDetail code reference and remediation guidance. --- # Platform API Authentication Platform API requests use a bearer credential in the `Authorization` header. Prefer OAuth for per-user integrations. Use a Glean-issued token for global permissions with `X-Glean-ActAs`, or when no OAuth path exists. ## Request header[​](#request-header "Direct link to Request header") ``` Authorization: Bearer ``` ## Choose an authentication method[​](#choose-an-authentication-method "Direct link to Choose an authentication method") ### Glean OAuth Authorization Server Use Glean's OAuth Authorization Server for OAuth access tokens and fine-grained Glean scopes. It is on by default. Tenants that already used IdP OAuth may have it off. DCR is subject to tenant policy; use a static client when DCR is disabled, when the tenant policy does not allow the application, or when the application needs scopes DCR does not grant. ### External IdP OAuth Use OAuth access tokens issued by a configured identity provider such as Google, Microsoft Entra ID, Okta, or OneLogin. ### Glean-issued tokens Use user-scoped or global bearer tokens created in the Glean Admin Console. Global tokens also require `X-Glean-ActAs`. ## Quick start[​](#quick-start "Direct link to Quick start") 1. Confirm that your application has access to the Platform API. 2. For a per-user integration, obtain an OAuth access token from the Glean OAuth Authorization Server or a configured external IdP. Use a Glean-issued token for global `X-Glean-ActAs`, or when no OAuth path exists. 3. Store the credential securely and provide it in the `Authorization` header. 4. Try the [Platform API Quickstart](/api/platform-api/getting-started.md). note Platform API and Client API use the same customer-facing authentication methods. Indexing API operations use Glean-issued tokens and do not accept OAuth. Follow the method-specific setup and scope requirements for the endpoint you are calling. ## Related resources[​](#related-resources "Direct link to Related resources") * [Platform API overview](/api/platform-api.md) * [Platform API reference](/api/platform-api/platform-search.md) * [Common API authentication concepts](/get-started/authentication.md) * [OAuth setup, static clients, and DCR conditions](/api-info/client/authentication/oauth.md) --- # Platform Chat API The Platform Chat API creates chat responses for applications that want to use Glean's knowledge and AI capabilities through the modern Platform API surface. ### [Create a chat response](/api/platform-api/platform-chat-create.md) [Send a user request and receive a response grounded in your Glean instance.](/api/platform-api/platform-chat-create.md) ### [Platform API authentication](/api/platform-api/authentication.md) [Configure the bearer credential used by Platform API requests.](/api/platform-api/authentication.md) See the [Create a chat response reference](/api/platform-api/platform-chat-create.md) for the request and response schema. --- # Platform API Quickstart The Platform API is Glean's recommended API for new application integrations when the capability and stability level fit the use case. It provides modern endpoints under the `/api` path. Review the applicable stability notices before adopting a capability or endpoint. ## Before you begin[​](#before-you-begin "Direct link to Before you begin") 1 Find your API URL If automatic discovery is unavailable, ask a Glean admin for the complete **Server instance (QE)** value. Glean admins can copy it from the [About Glean page](https://app.glean.com/admin/about-glean). 2 Configure authentication Review the [Platform API authentication guide](/api/platform-api/authentication.md) and choose the credentials for your integration. 3 Choose a capability Start with the capability that matches your application: [Search](/api/platform-api/search-overview.md), [Agents](/api/platform-api/agents-overview.md), [Skills](/api/platform-api/skills-overview.md), or [Chat](/api/platform-api/chat-overview.md). ## Choose Platform API over Client API[​](#choose-platform-api-over-client-api "Direct link to Choose Platform API over Client API") Use Platform API for new integrations whenever the capability you need is available. Use [Client API](/api/client-api.md) when you need an existing Client API capability that has not yet moved to Platform API or when maintaining an existing integration. ## Make your first request[​](#make-your-first-request "Direct link to Make your first request") The example below uses the Search API. Review the endpoint's stability notice before using it in production. The Platform API uses a bearer credential and JSON requests. A typical request looks like this: ``` curl -X POST 'https://-be.glean.com/api/search' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "query": "quarterly planning" }' ``` See the [Search API reference](/api/platform-api/platform-search.md) for the complete request and response model. ## Next steps[​](#next-steps "Direct link to Next steps") ### [Authenticate](/api/platform-api/authentication.md) [Configure one of the supported Platform API bearer-token methods.](/api/platform-api/authentication.md) ### [Search](/api/platform-api/search-overview.md) [Build permission-aware search experiences over connected content.](/api/platform-api/search-overview.md) ### [Run agents](/api/platform-api/agents-overview.md) [Discover agents and create agent runs.](/api/platform-api/agents-overview.md) --- # Cancel an agent run * Endpoint ``` POST /api/agents/:agent_id/cancellations ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `AGENTS` Request cooperative cancellation of the durable agent run identified by `run_id` in the JSON body. Requires ownership, current agent access, and the agents.run scope. Sending a cancellation signal does not itself change an active run from RUNNING; poll GET run for the final state. Paused runs become CANCELLED without resuming execution. Repeated requests and requests for terminal runs return the current snapshot. Completion may win a race with cancellation. Completed tool side effects cannot be undone, and external work may continue if a tool does not support cancellation. Cancellation targets this run, not separate background-subagent executions. An active run without a cancellation registration returns 409. Cancellation signaling requires Redis. An interrupted active run can instead become FAILED through deadline cleanup; this does not verify that external tool work has stopped. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 409 * 413 * 429 * 500 * 503 Current run snapshot after requesting cancellation, or an unchanged terminal run. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Create agent run * Endpoint ``` POST /api/agents/:agent_id/runs ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `AGENTS` Execute an agent run. By default, set `stream` to true to receive server-sent events; otherwise the response contains the final agent messages. Set `execution_mode` to `DURABLE` to persist a new run and return its initial snapshot with HTTP 201 without waiting for execution. Poll the agent-scoped GET run endpoint for progress. Durable execution continues after an HTTP disconnect, but is not automatically resumed after a QE restart or crash. An active turn becomes overdue more than 40 minutes after acceptance (a 30-minute execution timeout plus 10 minutes of grace). The next GET of the run marks the overdue turn FAILED without replay; there is no periodic sweep. Without a GET, the stored run can remain RUNNING. Failure does not prove that external tool work has stopped. Paused runs are not expired; an accepted approval continuation starts a fresh deadline. Each POST creates a new run; retrying a POST can create another execution. Submit pending approval decisions through the run responses endpoint, and cancellation can be requested through the run cancellations endpoint. A run tracks one workflow execution; automatic background-subagent wake turns are separate executions, not continuations tracked by this run ID. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 201 * 400 * 401 * 403 * 404 * 408 * 409 * 413 * 422 * 429 * 500 * 503 Successful response. Durable run persisted and started. The snapshot is immediately retrievable through GET /api/agents/{agent\_id}/runs/{run\_id}. Execution failures appear in the run state. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Request body exceeds the maximum allowed size. Returned when the agent has tools the caller must authorize before the run can start. `authentication_suggestions` names each such tool; POST its `server_id` to the Client API's `/tool-servers/{serverId}/auth` with `returnUrl` in the request body to obtain an `authorizationUrl` to redirect the end user to, then retry the run once OAuth completes. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Respond to agent run approvals * Endpoint ``` POST /api/agents/:agent_id/responses ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `AGENTS` Submit decisions for every pending tool approval in the paused run's current batch. The run is identified by `run_id` in the JSON body. Decisions apply only to the stored invocations and arguments; argument edits, authentication responses, and session-wide grants are not supported. The caller must own the run, still have agent access, and have the agents.run scope. Acceptance persists the decisions before resuming the same run and chat session. Identical accepted decisions return the current snapshot without another continuation. Conflicting, stale, incomplete, or non-pending decisions return 409. Cancellation registration failure returns 503 without accepting the decisions; retry the same approval batch. This retry guarantee does not cover an indeterminate database commit outcome. Go workflow approval resumes currently support one tool invocation and one approval response. Unsupported multi-tool or multi-decision Go resumes fail without executing tools. The resumed action must resolve to the tool identified by the stored approval request and paused checkpoint. Missing or inconsistent identity fails without executing tools. Execution continues after HTTP disconnects, but is not automatically resumed after a QE crash. Each accepted continuation starts a fresh 30-minute execution timeout and 40-minute cleanup deadline. Identical retries do not extend that deadline. Waiting for approval does not expire a run. The next GET marks an overdue active turn FAILED without replaying execution. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 409 * 413 * 429 * 500 * 503 Decisions accepted, or an identical retry. Poll GET run for continued execution. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Get agent * Endpoint ``` GET /api/agents/:agent_id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `AGENTS` Retrieve details for an agent available to the authenticated user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Get agent run * Endpoint ``` GET /api/agents/:agent_id/runs/:run_id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `AGENTS` Retrieve a persisted workflow execution owned by the authenticated user. The run must belong to the specified agent, and the user must still have access to that agent. Unknown runs, runs owned by another user, and mismatched agent/run identifiers return 404. Requires the agents.run scope. Executions without a persisted workflow record are not available through this endpoint. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Current persisted run snapshot. A failed execution is still a successful retrieval. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Get agent schemas * Endpoint ``` GET /api/agents/:agent_id/schemas ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `AGENTS` Retrieve an agent's input and output JSON schemas. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Search agents * Endpoint ``` POST /api/agents/search ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `AGENTS` Search agents available to the authenticated user by agent name. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 413 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Create a chat response * Endpoint ``` POST /api/chat ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `CHAT` Run an assistant turn. The default response is JSON. HTTP clients request server-sent events by setting `stream` to true in the JSON body. An `Accept: text/event-stream` header does not replace `stream`. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 413 * 422 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Syntactically valid but semantically incorrect request. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Create a streaming chat response * Endpoint ``` POST /api/chat#stream ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `CHAT` SDK-only logical operation. HTTP clients must call the base path; the URL fragment is not sent. Run an assistant turn as server-sent events. HTTP clients request this mode by setting `stream` to true in the JSON body. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 413 * 422 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Syntactically valid but semantically incorrect request. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Search * Endpoint ``` POST /api/search ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SEARCH` Search your organization's connected content and return ranked document results with cursor pagination. Use `GET /api/search/filters` to discover datasource identifiers and common filter fields. Built-in filter names are validated; other field names are accepted as custom filters and behavior depends on your Glean configuration and connected sources. Errors: HTTP 422 `unprocessable_query` returns no `results` or `next_cursor`. See `warnings` on the response for non-blocking issues such as partially available results. Not every query issue produces a warning or error. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 413 * 422 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Syntactically valid but semantically incorrect request. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # List search filters * Endpoint ``` GET /api/search/filters ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SEARCH` List datasources and common built-in filter fields visible to the authenticated user. This is a best-effort catalog, not an exhaustive list of every filter search accepts. Without `query`, returns field metadata only and does not run a search. With a nonblank `query`, provide exactly one `datasources` value to request suggested filter values for that query; no documents are returned and this endpoint does not include warning objects. See `FilterFieldInfo.values` for limitations on suggested values. Rate-limited requests return HTTP 429 with `Retry-After`; temporary backend unavailability returns HTTP 503. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. **Response Headers** * **Cache-Control** Present as `no-store` for query-backed discovery responses. Catalog-only responses omit this endpoint-specific override. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Create skill * Endpoint ``` POST /api/skills ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Create a skill from an uploaded SKILL.md, .zip, or .skill bundle. If the authenticated user already has a skill with the same name, the existing skill is superseded with a new version, unless it is source-managed: a same-name create over a GitHub-imported skill returns 409, and the caller syncs the existing skill instead. Two concurrent same-name creates can still produce two skills. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 201 * 400 * 401 * 403 * 404 * 408 * 409 * 413 * 429 * 500 * 503 Skill created. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Create skill version * Endpoint ``` POST /api/skills/:skill_id/versions ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Create a new immutable version for an existing caller-managed skill from an uploaded SKILL.md, .zip, or .skill bundle. A create-version over a GitHub-imported skill returns 409, and the caller syncs the existing skill instead. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 201 * 400 * 401 * 403 * 404 * 408 * 409 * 413 * 429 * 500 * 503 Skill version created. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Delete skill * Endpoint ``` DELETE /api/skills/:skill_id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Delete a skill the authenticated caller is allowed to manage. This operation permanently removes all versions of the skill. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 204 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Skill deleted. No content. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Retrieve skill * Endpoint ``` GET /api/skills/:skill_id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Retrieve metadata for a skill available to the authenticated user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Download skill content * Endpoint ``` GET /api/skills/:skill_id/content ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Download the latest installable bundle for a skill available to the authenticated user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. **Response Headers** * **Content-Disposition** Attachment filename for the skill bundle. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Retrieve skill version * Endpoint ``` GET /api/skills/:skill_id/versions/:version ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Retrieve metadata for a skill version available to the authenticated user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Download skill version content * Endpoint ``` GET /api/skills/:skill_id/versions/:version/content ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Download the installable bundle for a skill version available to the authenticated user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. **Response Headers** * **Content-Disposition** Attachment filename for the skill bundle. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Import skills from GitHub * Endpoint ``` POST /api/skills/import ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Import one or more skills selected from a GitHub source preview. Each source URL is fetched and persisted as an independent skill with source provenance. This operation does not create a durable source resource. The import is atomic: if any source cannot be fetched, validated, or persisted, no skills are created. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 201 * 400 * 401 * 403 * 408 * 409 * 413 * 422 * 429 * 500 * 503 Skills imported successfully. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Request body exceeds the maximum allowed size. Returned when GitHub authorization is missing for a source URL. `authentication_suggestions` names the GitHub action pack to authorize; POST its `server_id` to the Client API's `/tool-servers/{serverId}/auth` with `returnUrl` in the request body to obtain an `authorizationUrl` to redirect the end user to, then retry the import once OAuth completes. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # List skills * Endpoint ``` GET /api/skills ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` List every custom skill the authenticated caller can access. Built-in skills are excluded: they have no versions, content download, update, or delete, so their identifiers would fail most skill operations. Chat-authored skills shared with the caller without a listed grant are omitted: they stay retrievable by identifier when it is known, but this list does not discover them. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # List skill versions * Endpoint ``` GET /api/skills/:skill_id/versions ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` List versions for a skill available to the authenticated user. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Preview a GitHub skill source * Endpoint ``` POST /api/skills/sources/preview ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Inspect a GitHub URL without persisting a source or any discovered skills. Set stream to true to receive repository scan progress as server-sent events; otherwise the response contains the completed preview. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 408 * 413 * 422 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Returned when GitHub authorization is missing for the source URL. `authentication_suggestions` names the GitHub action pack to authorize; POST its `server_id` to the Client API's `/tool-servers/{serverId}/auth` with `returnUrl` in the request body to obtain an `authorizationUrl` to redirect the end user to, then retry the preview once OAuth completes. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Preview a GitHub skill source as events * Endpoint ``` POST /api/skills/sources/preview#stream ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` SDK-only logical operation. HTTP clients must call the base path; the URL fragment is not sent. Inspect a GitHub URL as server-sent events. HTTP clients request this mode by setting `stream` to true in the JSON body. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 408 * 413 * 422 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Returned when GitHub authorization is missing for the source URL. `authentication_suggestions` names the GitHub action pack to authorize; POST its `server_id` to the Client API's `/tool-servers/{serverId}/auth` with `returnUrl` in the request body to obtain an `authorizationUrl` to redirect the end user to, then retry the preview once OAuth completes. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Sync a GitHub-imported skill * Endpoint ``` POST /api/skills/:skill_id/sync ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Refresh one GitHub-imported skill from its stored source URL. If the skill content has changed, this operation creates a new skill version. If the skill is no longer present upstream, the stored skill is left unchanged and must be deleted explicitly. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 409 * 422 * 429 * 500 * 503 Skill sync completed successfully. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Returned when GitHub authorization is missing for the stored source URL. `authentication_suggestions` names the GitHub action pack to authorize; POST its `server_id` to the Client API's `/tool-servers/{serverId}/auth` with `returnUrl` in the request body to obtain an `authorizationUrl` to redirect the end user to, then retry the sync once OAuth completes. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Update skill * Endpoint ``` PATCH /api/skills/:skill_id ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Enable or disable the skill for the authenticated caller without changing its content. The owner's update sets the skill's stored status. Any other caller's update applies only to that caller. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 409 * 413 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Validate skill bundle * Endpoint ``` POST /api/skills/validation ``` * Release stage Generally available * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `SKILLS` Validate a skill bundle without persisting it. Accepts a SKILL.md, .zip, or .skill upload and returns parsed metadata plus the normalized file layout. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 413 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Search events for a trigger preset * Endpoint ``` POST /api/trigger-presets/:preset_id/events/search ``` * Release stage ExperimentalIntroduced August 12, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` Search recent content events an unsaved trigger built from this preset would match, to preview it before creating the trigger. Read-only — no trigger is created and no webhook delivery is made. Covers the last seven days. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 413 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Get trigger preset * Endpoint ``` GET /api/trigger-presets/:preset_id ``` * Release stage ExperimentalIntroduced July 22, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` Retrieve a single trigger preset by id. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Search trigger preset input values * Endpoint ``` GET /api/trigger-presets/:preset_id/input-values ``` * Release stage ExperimentalIntroduced August 6, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` Return up to 300 selectable values for a single picklist input on a preset. Results are intended for typeahead selection and are not cursor-paginated. When `is_truncated` is true, refine `query` to narrow the result set. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # List trigger presets * Endpoint ``` GET /api/trigger-presets ``` * Release stage ExperimentalIntroduced July 22, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` List the trigger presets available to the caller. A preset is a curated content-trigger template (e.g. a new Jira ticket) which is passed when creating a trigger. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Create trigger * Endpoint ``` POST /api/triggers ``` * Release stage ExperimentalIntroduced July 6, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` Create a trigger from a preset and return it with its signing secret. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 201 * 400 * 401 * 403 * 404 * 408 * 409 * 413 * 429 * 500 * 503 Trigger created. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request conflicts with current state of the resource. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. ## Callbacks[​](#callbacks "Direct link to Callbacks") * POST triggerEvent ``` POST {$request.body#/delivery/webhook_url} ``` Delivery uses at-least-once semantics, so the same event can be sent more than once. Return any 2xx status after accepting the event. Network failures, 408, 429, and 5xx responses are retried; other non-2xx responses, including redirects, are terminal. The `webhook-id` remains stable across attempts and can be used for deduplication, while `webhook-timestamp` and `webhook-signature` are generated for each attempt. ## Callbacks Responses[​](#callbacks-responses "Direct link to Callbacks Responses") * 408 * 429 * 2XX * 3XX * 4XX * 5XX Request timeout. Glean retries the delivery. Rate limited. Glean retries the delivery. Event accepted. Glean does not retry the delivery. Redirects are not followed and the delivery is not retried. Client error other than 408 or 429. Glean does not retry. Server error. Glean retries the delivery. --- # Delete trigger * Endpoint ``` DELETE /api/triggers/:trigger_id ``` * Release stage ExperimentalIntroduced July 6, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` Delete a trigger. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 204 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Trigger deleted. No content. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Search events for a trigger * Endpoint ``` POST /api/triggers/:trigger_id/events/search ``` * Release stage ExperimentalIntroduced August 7, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` Search recent content events an existing trigger matches. Read-only — no webhook delivery is made. Covers the last seven days. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 413 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Get trigger * Endpoint ``` GET /api/triggers/:trigger_id ``` * Release stage ExperimentalIntroduced July 6, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` Retrieve a trigger owned by the authenticated caller. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # List triggers * Endpoint ``` GET /api/triggers ``` * Release stage ExperimentalIntroduced July 6, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` List triggers owned by the authenticated caller. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 408 * 429 * 500 * 503 Successful response. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Backend did not respond within the timeout window. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Update trigger * Endpoint ``` PATCH /api/triggers/:trigger_id ``` * Release stage ExperimentalIntroduced July 6, 2026 · [How experimental APIs work](/experimental/overview.md) * Authorization `Authorization: Bearer `OAuth access token or Glean-issued token · [Authentication](/api/platform-api/authentication.md) * Scopes `TRIGGERS` Update a trigger. ## Request[​](#request "Direct link to request") ## Responses[​](#responses "Direct link to Responses") * 200 * 400 * 401 * 403 * 404 * 408 * 413 * 429 * 500 * 503 Trigger updated. Invalid request (malformed JSON, invalid parameter values, unknown fields). Missing or invalid authentication token. Token valid but lacks permission for the requested operation. Resource not found. Backend did not respond within the timeout window. Request body exceeds the maximum allowed size. Rate limit exceeded. Includes Retry-After header. Unexpected server-side failure. Backend temporarily unavailable. --- # Search API The Platform Search API returns relevant results from across your organization's connected content, with support for filters and time ranges. Use it to power search experiences built on Glean's unified index. ## Use Cases ### Enterprise Search Query across all connected datasources to surface the most relevant documents, messages, and records for a user. ### Filtered Retrieval Apply filters and time ranges to scope results to specific datasources, owners, or recency windows. ## API Endpoints | Method | Endpoint | Purpose | | ------ | --------------------------------------------------------------------- | ------------------------------------------------------------------- | | POST | [`/api/search`](/api/platform-api/platform-search.md) | [Search](/api/platform-api/platform-search.md) | | GET | [`/api/search/filters`](/api/platform-api/platform-search-filters.md) | [List search filters](/api/platform-api/platform-search-filters.md) | For error responses, see [Platform API Errors](/errors/.md) for the shared ProblemDetail code reference and remediation guidance. --- # Skills API The Platform Skills API lets applications discover, validate, import, synchronize, version, and manage skills that can be used with Glean. These endpoints are generally available. A request does not need the `X-Glean-Include-Experimental` header. ## Workspace access[​](#workspace-access "Direct link to Workspace access") Operations that read or write a stored skill return [`403`](/errors/insufficient-permissions.md) `insufficient_permissions` when the caller does not have access to Skills in the workspace. The response `detail` is `Skills is not enabled for this workspace.` That check runs on: * `GET /api/skills` and `POST /api/skills` * `GET`, `PATCH`, and `DELETE /api/skills/{skill_id}` * `GET /api/skills/{skill_id}/content` * `GET` and `POST /api/skills/{skill_id}/versions` * `GET /api/skills/{skill_id}/versions/{version}` * `GET /api/skills/{skill_id}/versions/{version}/content` `POST /api/skills` then checks whether the caller can create skills. Without that permission the `detail` is `You do not have permission to create skills.` `POST /api/skills/validation` checks a bundle's structure and metadata and does not read or write stored skills, so it does not apply the workspace access check. GitHub import and sync use one denial for a workspace where GitHub skill import is off and for a caller without Skills access: * `POST /api/skills/import` returns `detail` `You do not have permission to import skills from GitHub.` * `POST /api/skills/{skill_id}/sync` returns `detail` `You do not have permission to sync skills from GitHub.` Source preview checks the two conditions separately. When GitHub skill import is off, `POST /api/skills/sources/preview` returns `detail` `GitHub skill import is not enabled for this workspace.` When GitHub skill import is on and the caller does not have access to Skills, the `detail` is `You do not have permission to preview skill sources.` Import and preview also return `You do not have permission to create skills.` when the caller can reach the operation but cannot create skills. ## Explore Skills[​](#explore-skills "Direct link to Explore Skills") ### [Discover skills](/api/platform-api/platform-skills-list.md) [List the skills available to your application.](/api/platform-api/platform-skills-list.md) ### [Manage skills](/api/platform-api/platform-skills-create.md) [Create, update, import, synchronize, and delete skills.](/api/platform-api/platform-skills-create.md) ### [Validate skill bundles](/api/platform-api/platform-skills-validate.md) [Check a skill bundle's structure and metadata without persisting it.](/api/platform-api/platform-skills-validate.md) ### [Manage versions](/api/platform-api/platform-skills-list-versions.md) [Create and inspect skill versions and their content.](/api/platform-api/platform-skills-list-versions.md) Browse the complete [Skills API reference](/api/platform-api/platform-skills-list.md). For error responses, see [Platform API Errors](/errors/.md) for the shared ProblemDetail code reference and remediation guidance. --- # Triggers API The Platform Triggers API lets an application subscribe to content events in Glean and receive them at its own endpoint as signed webhooks. A trigger is created from a **preset** — a curated event definition such as "a review is requested from me" or "30 minutes before an event starts" — so you describe what you care about rather than assembling a query. Events are delivered as [Standard Webhooks](https://www.standardwebhooks.com), signed with HMAC-SHA256, and are scoped to what the subscribing user is permitted to see. These endpoints are [experimental](/experimental/overview.md) — send `X-Glean-Include-Experimental: true` on each request, or they are not exposed. ## Explore Triggers[​](#explore-triggers "Direct link to Explore Triggers") ### [Browse presets](/api/platform-api/platform-trigger-presets-list.md) [List the event definitions your deployment serves, and the inputs each one accepts.](/api/platform-api/platform-trigger-presets-list.md) ### [Create a trigger](/api/platform-api/platform-triggers-create.md) [Instantiate a preset against your webhook URL and receive its signing secret.](/api/platform-api/platform-triggers-create.md) ### [Preview matching events](/api/platform-api/platform-trigger-presets-events-search.md) [See which recent events a preset would have matched, before you create anything.](/api/platform-api/platform-trigger-presets-events-search.md) ### [Manage triggers](/api/platform-api/platform-triggers-list.md) [List, update, and delete the triggers you own.](/api/platform-api/platform-triggers-list.md) Browse the complete [Triggers API reference](/api/platform-api/platform-trigger-presets-list.md). --- # Changelog[](/changelog.xml "Subscribe to RSS feed") Keep up with the latest changes and updates to Glean's developer platform. CategoryAll categories (all) AttentionAll attention levels (all) 372 of 372 October 2 2026 ## Platform Skills API is generally available APIFeature The Platform Skills API is generally available. Skills requests do not need the `X-Glean-Include-Experimental` header. **Read more** ## What changed * Skills endpoints are stable. Omit `X-Glean-Include-Experimental` on Skills requests. * Operations that read or write a stored skill return `403` `insufficient_permissions` with `detail` `Skills is not enabled for this workspace.` when the caller does not have access to Skills in the workspace. * `POST /api/skills/validation` checks a bundle without reading or writing stored skills, and it does not apply that workspace check. * GitHub import, sync, and source preview keep their own permission details. See the [Skills API overview](/api/platform-api/skills-overview.md). Triggers remain experimental and still require `X-Glean-Include-Experimental: true`. ## Documentation * [Skills API overview](/api/platform-api/skills-overview.md) * [List skills](/api/platform-api/platform-skills-list.md) September 23 2026 ## api-client-typescript v0.20.15 API Clients Api-client-typescript v0.20.15 includes 4 additions, 2 changes. **Read more** ## Changes * Added `request.platformAgentRunCreateRequest.executionMode` to `agents.createrun()`. * Added `response.status[201]` to `agents.createrun()`. * Added `request.text` to `chat.create()`. * Added `response.output[].content[].structuredOutput` to `chat.create()`. * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.20.15) ## api-client-python v0.17.15 API Clients Api-client-python v0.17.15 includes 4 additions, 2 changes. **Read more** ## Changes * Added `request.execution_mode` to `agents.create_run()`. * Added `response.status[201]` to `agents.create_run()`. * Added `request.text` to `chat.create()`. * Added `response.output[].content[].structured_output` to `chat.create()`. * Changed `request.categories[]` on `client.search.retrieve_feed()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.17.15) ## api-client-java v0.17.16 API Clients Api-client-java v0.17.16 includes 4 additions, 2 changes. **Read more** ## Changes * Added `request.platformAgentRunCreateRequest.executionMode` to `agents.createrun()`. * Added `response.status[201]` to `agents.createrun()`. * Added `request.text` to `chat.create()`. * Added `response.output[].content[].structuredOutput` to `chat.create()`. * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.17.16) ## api-client-go v0.15.15 API Clients Api-client-go v0.15.15 includes 4 additions, 2 changes. **Read more** ## Changes * Added `request.PlatformAgentRunCreateRequest.ExecutionMode` to `agents.createrun()`. * Added `response.status[201]` to `agents.createrun()`. * Added `request.Request.Text` to `chat.create()`. * Added `response.Output[].Content[].StructuredOutput` to `chat.create()`. * Changed `request.FeedRequest.Categories[]` on `client.search.retrievefeed()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.15.15) September 18 2026 ## api-client-typescript v0.20.14 API Clients Api-client-typescript v0.20.14 includes 2 additions, 3 changes. **Read more** ## Changes * Added `response.run.messages[].content[].annotations` to `agents.createrun()`. * Added `response.run.messages[].content[].annotations` to `client.agents.run()`. * Changed `request.importAgentRequest` on `client.agents.import()`. * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.20.14) ## api-client-python v0.17.14 API Clients Api-client-python v0.17.14 includes 2 additions, 3 changes. **Read more** ## Changes * Added `response.run.messages[].content[].annotations` to `agents.create_run()`. * Added `response.run.messages[].content[].annotations` to `client.agents.run()`. * Changed `request` on `client.agents.import()`. * Changed `request.categories[]` on `client.search.retrieve_feed()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.17.14) ## api-client-java v0.17.15 API Clients Api-client-java v0.17.15 includes 2 additions, 3 changes. **Read more** ## Changes * Added `response.run.messages[].content[].annotations` to `agents.createrun()`. * Added `response.run.messages[].content[].annotations` to `client.agents.run()`. * Changed `request.importAgentRequest` on `client.agents.import()`. * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.17.15) ## api-client-go v0.15.14 API Clients Api-client-go v0.15.14 includes 2 additions, 3 changes. **Read more** ## Changes * Added `response.Run.Messages[].Content[].Annotations` to `agents.createrun()`. * Added `response.Run.Messages[].Content[].Annotations` to `client.agents.run()`. * Changed `request.ImportAgentRequest` on `client.agents.import()`. * Changed `request.FeedRequest.Categories[]` on `client.search.retrievefeed()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.15.14) September 9 2026 ## api-client-typescript v0.20.9 API Clients Api-client-typescript v0.20.9 includes 1 addition, 1 change. **Read more** ## Changes * Added `request.feedRequest.categories[].enum(editDoc)` to `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.20.9) ## api-client-python v0.17.9 API Clients Api-client-python v0.17.9 includes 1 addition, 1 change. **Read more** ## Changes * Added `request.categories[].enum(edit_doc)` to `client.search.retrieve_feed()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.17.9) ## api-client-java v0.17.10 API Clients Api-client-java v0.17.10 includes 1 addition, 1 change. **Read more** ## Changes * Added `request.feedRequest.categories[].enum(editDoc)` to `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.17.10) ## api-client-go v0.15.9 API Clients Api-client-go v0.15.9 includes 1 addition, 1 change. **Read more** ## Changes * Added `request.FeedRequest.Categories[].Enum(editDoc)` to `client.search.retrievefeed()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.15.9) September 7 2026 ## glean-indexing-sdk v1.2.0 Glean Indexing SDK Glean-indexing-sdk v1.2.0: Identify Indexing SDK requests. **Read more** ## Changes * Identify Indexing SDK requests. * **deployment**: harden managed connector workflows. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v1.2.0) * [PR #206](https://github.com/gleanwork/glean-indexing-sdk/pull/206) * [PR #201](https://github.com/gleanwork/glean-indexing-sdk/pull/201) September 6 2026 ## api-client-typescript v0.20.5 API Clients Api-client-typescript v0.20.5 includes 2 changes. **Read more** ## Changes * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.20.5) ## api-client-python v0.17.4 API Clients Api-client-python v0.17.4 includes 2 changes. **Read more** ## Changes * Changed `request.categories[]` on `client.search.retrieve_feed()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.17.4) ## api-client-java v0.17.5 API Clients Api-client-java v0.17.5 includes 2 changes. **Read more** ## Changes * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.17.5) ## api-client-go v0.15.5 API Clients Api-client-go v0.15.5 includes 2 changes. **Read more** ## Changes * Changed `request.FeedRequest.Categories[]` on `client.search.retrievefeed()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.15.5) September 3 2026 ## api-client-typescript v0.20.3 API Clients Api-client-typescript v0.20.3 includes 54 additions. **Read more** ## Changes * Added `request.createAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.announcements.create()`. * Added `response.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.announcements.create()`. * Added `request.updateAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.announcements.update()`. * Added `response.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.announcements.update()`. * Added `request.createAnswerRequest.data.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.create()`. * Added `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.create()`. * Added `request.editAnswerRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.update()`. * Added `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.update()`. * Added `response.answerResult.answer.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.retrieve()`. * Added `response.answerResults[].answer.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.list()`. * Added `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.chat.create()`. * Added `response.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.chat.create()`. * Added `response.chatResult.chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.chat.retrieve()`. * Added `error.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.chat.retrieve()`. * Added `response.chatResults[].chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.chat.list()`. * Added `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.chat.createstream()`. * Added `response.workflow.author.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.agents.create()`. * Added `response.workflowResult.workflow.author.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.agents.import()`. * Added `response.collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.additems()`. * Added `request.createCollectionRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.create()`. * Added `response.union(class (0)).collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.create()`. * Added `response.collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.deleteitem()`. * Added `request.editCollectionRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.update()`. * Added `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.update()`. * Added `response.collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.updateitem()`. * Added `response.collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.retrieve()`. * Added `response.collections[].addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.list()`. * Added `response.documents.Map.union(Document).metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.documents.retrieve()`. * Added `response.documents[].metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.documents.retrievebyfacets()`. * Added `response.gleanAssist.activityInsights[].user.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.insights.retrieve()`. * Added `response.searchResponse.results[].structuredResults[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.messages.retrieve()`. * Added `response.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.pins.update()`. * Added `response.pin.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.pins.retrieve()`. * Added `response.pins[].attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.pins.list()`. * Added `response.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.pins.create()`. * Added `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.search.queryasadmin()`. * Added `response.results[].structuredResults[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.search.queryasadmin()`. * Added `response.results[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.search.autocomplete()`. * Added `response.results[].primaryEntry.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.search.retrievefeed()`. * Added `request.recommendationsRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.search.recommendations()`. * Added `response.results[].structuredResults[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.search.recommendations()`. * Added `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.search.query()`. * Added `response.results[].structuredResults[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.search.query()`. * Added `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.entities.list()`. * Added `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.entities.readpeople()`. * Added `request.createShortcutRequest.data.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.retrieve()`. * Added `response.shortcuts[].addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.list()`. * Added `request.updateShortcutRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.update()`. * Added `response.shortcut.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.update()`. * Added `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.verification.addreminder()`. * Added `response.documents[].metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.verification.list()`. * Added `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.20.3) ## api-client-python v0.17.2 API Clients Api-client-python v0.17.2 includes 54 additions. **Read more** ## Changes * Added `request.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.announcements.create()`. * Added `response.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.announcements.create()`. * Added `request.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.announcements.update()`. * Added `response.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.announcements.update()`. * Added `request.data.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.answers.create()`. * Added `response.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.answers.create()`. * Added `request.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.answers.update()`. * Added `response.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.answers.update()`. * Added `response.answer_result.answer.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.answers.retrieve()`. * Added `response.answer_results[].answer.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.answers.list()`. * Added `request.messages[].citations[].source_document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.chat.create()`. * Added `response.messages[].citations[].source_document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.chat.create()`. * Added `response.chat_result.chat.created_by.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.chat.retrieve()`. * Added `error.created_by.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.chat.retrieve()`. * Added `response.chat_results[].chat.created_by.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.chat.list()`. * Added `request.messages[].citations[].source_document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.chat.create_stream()`. * Added `response.workflow.author.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.agents.create()`. * Added `response.workflow_result.workflow.author.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.agents.import()`. * Added `response.collection.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.add_items()`. * Added `request.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.create()`. * Added `response.union(class (0)).collection.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.create()`. * Added `response.collection.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.delete_item()`. * Added `request.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.update()`. * Added `response.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.update()`. * Added `response.collection.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.update_item()`. * Added `response.collection.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.retrieve()`. * Added `response.collections[].added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.collections.list()`. * Added `response.documents.Map.union(Document).metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.documents.retrieve()`. * Added `response.documents[].metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.documents.retrieve_by_facets()`. * Added `response.glean_assist.activity_insights[].user.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.insights.retrieve()`. * Added `response.search_response.results[].structured_results[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.messages.retrieve()`. * Added `response.attribution.related_documents[].query_suggestion.ranges[].document.metadata.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.pins.update()`. * Added `response.pin.attribution.related_documents[].query_suggestion.ranges[].document.metadata.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.pins.retrieve()`. * Added `response.pins[].attribution.related_documents[].query_suggestion.ranges[].document.metadata.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.pins.list()`. * Added `response.attribution.related_documents[].query_suggestion.ranges[].document.metadata.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.pins.create()`. * Added `request.source_document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.search.query_as_admin()`. * Added `response.results[].structured_results[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.search.query_as_admin()`. * Added `response.results[].document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.search.autocomplete()`. * Added `response.results[].primary_entry.created_by.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.search.retrieve_feed()`. * Added `request.source_document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.search.recommendations()`. * Added `response.results[].structured_results[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.search.recommendations()`. * Added `request.source_document.metadata.author.related_documents[].results[].structured_results[].answer.collections[].items[].shortcut.favorite_info.ugc_type.enum(skills_type)` to `client.search.query()`. * Added `response.results[].structured_results[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.search.query()`. * Added `response.results[].related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.entities.list()`. * Added `response.results[].related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.entities.read_people()`. * Added `request.data.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.shortcuts.create()`. * Added `response.shortcut.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.shortcuts.create()`. * Added `response.shortcut.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.shortcuts.retrieve()`. * Added `response.shortcuts[].added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.shortcuts.list()`. * Added `request.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.shortcuts.update()`. * Added `response.shortcut.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.shortcuts.update()`. * Added `response.metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.verification.add_reminder()`. * Added `response.documents[].metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.verification.list()`. * Added `response.metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata.pins[].favorite_info.ugc_type.enum(skills_type)` to `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.17.2) ## api-client-java v0.17.3 API Clients Api-client-java v0.17.3 includes 54 additions. **Read more** ## Changes * Added `request.createAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.announcements.create()`. * Added `response.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.announcements.create()`. * Added `request.updateAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.announcements.update()`. * Added `response.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.announcements.update()`. * Added `request.createAnswerRequest.data.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.create()`. * Added `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.create()`. * Added `request.editAnswerRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.update()`. * Added `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.update()`. * Added `response.answerResult.answer.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.retrieve()`. * Added `response.answerResults[].answer.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.answers.list()`. * Added `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.chat.create()`. * Added `response.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.chat.create()`. * Added `response.chatResult.chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.chat.retrieve()`. * Added `error.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.chat.retrieve()`. * Added `response.chatResults[].chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.chat.list()`. * Added `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.chat.createstream()`. * Added `response.workflow.author.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.agents.create()`. * Added `response.workflowResult.workflow.author.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.agents.import()`. * Added `response.collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.additems()`. * Added `request.createCollectionRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.create()`. * Added `response.union(class (0)).collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.create()`. * Added `response.collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.deleteitem()`. * Added `request.editCollectionRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.update()`. * Added `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.update()`. * Added `response.collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.updateitem()`. * Added `response.collection.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.retrieve()`. * Added `response.collections[].addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.collections.list()`. * Added `response.documents.Map.union(Document).metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.documents.retrieve()`. * Added `response.documents[].metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.documents.retrievebyfacets()`. * Added `response.gleanAssist.activityInsights[].user.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.insights.retrieve()`. * Added `response.searchResponse.results[].structuredResults[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.messages.retrieve()`. * Added `response.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.pins.update()`. * Added `response.pin.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.pins.retrieve()`. * Added `response.pins[].attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.pins.list()`. * Added `response.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.pins.create()`. * Added `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.search.queryasadmin()`. * Added `response.results[].structuredResults[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.search.queryasadmin()`. * Added `response.results[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.search.autocomplete()`. * Added `response.results[].primaryEntry.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.search.retrievefeed()`. * Added `request.recommendationsRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.search.recommendations()`. * Added `response.results[].structuredResults[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.search.recommendations()`. * Added `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer.collections[].items[].shortcut.favoriteInfo.ugcType.enum(skillsType)` to `client.search.query()`. * Added `response.results[].structuredResults[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.search.query()`. * Added `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.entities.list()`. * Added `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.entities.readpeople()`. * Added `request.createShortcutRequest.data.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.retrieve()`. * Added `response.shortcuts[].addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.list()`. * Added `request.updateShortcutRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.update()`. * Added `response.shortcut.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.shortcuts.update()`. * Added `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.verification.addreminder()`. * Added `response.documents[].metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.verification.list()`. * Added `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.pins[].favoriteInfo.ugcType.enum(skillsType)` to `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.17.3) ## api-client-go v0.15.3 API Clients Api-client-go v0.15.3 includes 54 additions. **Read more** ## Changes * Added `request.CreateAnnouncementRequest.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.announcements.create()`. * Added `response.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.announcements.create()`. * Added `request.UpdateAnnouncementRequest.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.announcements.update()`. * Added `response.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.announcements.update()`. * Added `request.CreateAnswerRequest.Data.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.answers.create()`. * Added `response.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.answers.create()`. * Added `request.EditAnswerRequest.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.answers.update()`. * Added `response.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.answers.update()`. * Added `response.AnswerResult.Answer.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.answers.retrieve()`. * Added `response.AnswerResults[].Answer.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.answers.list()`. * Added `request.ChatRequest.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.chat.create()`. * Added `response.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.chat.create()`. * Added `response.ChatResult.Chat.CreatedBy.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.chat.retrieve()`. * Added `error.CreatedBy.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.chat.retrieve()`. * Added `response.ChatResults[].Chat.CreatedBy.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.chat.list()`. * Added `request.ChatRequest.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.chat.createstream()`. * Added `response.Workflow.Author.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.agents.create()`. * Added `response.WorkflowResult.Workflow.Author.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.agents.import()`. * Added `response.Collection.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.additems()`. * Added `request.CreateCollectionRequest.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.create()`. * Added `response.union(class (0)).Collection.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.create()`. * Added `response.Collection.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.deleteitem()`. * Added `request.EditCollectionRequest.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.update()`. * Added `response.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.update()`. * Added `response.Collection.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.updateitem()`. * Added `response.Collection.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.retrieve()`. * Added `response.Collections[].AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.collections.list()`. * Added `response.Documents.Map.union(Document).Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.documents.retrieve()`. * Added `response.Documents[].Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.documents.retrievebyfacets()`. * Added `response.GleanAssist.ActivityInsights[].User.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.insights.retrieve()`. * Added `response.SearchResponse.Results[].StructuredResults[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.messages.retrieve()`. * Added `response.Attribution.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.pins.update()`. * Added `response.Pin.Attribution.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.pins.retrieve()`. * Added `response.Pins[].Attribution.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.pins.list()`. * Added `response.Attribution.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.pins.create()`. * Added `request.SearchRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.search.queryasadmin()`. * Added `response.Results[].StructuredResults[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.search.queryasadmin()`. * Added `response.Results[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.search.autocomplete()`. * Added `response.Results[].PrimaryEntry.CreatedBy.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.search.retrievefeed()`. * Added `request.RecommendationsRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.search.recommendations()`. * Added `response.Results[].StructuredResults[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.search.recommendations()`. * Added `request.SearchRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer.Collections[].Items[].Shortcut.FavoriteInfo.UgcType.Enum(skillsType)` to `client.search.query()`. * Added `response.Results[].StructuredResults[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.search.query()`. * Added `response.Results[].RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.entities.list()`. * Added `response.Results[].RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.entities.readpeople()`. * Added `request.CreateShortcutRequest.Data.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.shortcuts.create()`. * Added `response.Shortcut.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.shortcuts.create()`. * Added `response.Shortcut.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.shortcuts.retrieve()`. * Added `response.Shortcuts[].AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.shortcuts.list()`. * Added `request.UpdateShortcutRequest.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.shortcuts.update()`. * Added `response.Shortcut.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.shortcuts.update()`. * Added `response.Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.verification.addreminder()`. * Added `response.Documents[].Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.verification.list()`. * Added `response.Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Pins[].FavoriteInfo.UgcType.Enum(skillsType)` to `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.15.3) September 2 2026 ## api-client-typescript v0.20.0 API ClientsBreakingAction required Api-client-typescript v0.20.0 includes 1 breaking removal, 1 change. **Read more** ## Action Required * Update callers of `chat.create()` to stop using `response.status[200].content[text/event-stream]`. ## Changes * Changed `request.stream` on `chat.create()`. ## Breaking Changes * Removed `response.status[200].content[text/event-stream]` from `chat.create()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.20.0) ## api-client-python v0.17.0 API ClientsBreakingAction requiredDeprecated Api-client-python v0.17.0 includes 2 breaking removals. **Read more** ## Action Required * Update callers of `chat.create()` to stop using `request.stream`. * Update callers of `chat.create()` to stop using `response.status[200].content[text/event-stream]`. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Removed `request.stream` from `chat.create()`. * Removed `response.status[200].content[text/event-stream]` from `chat.create()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.17.0) ## api-client-java v0.17.0 API ClientsBreakingAction required Api-client-java v0.17.0 includes 1 breaking removal, 1 change. **Read more** ## Action Required * Update callers of `chat.create()` to stop using `response.status[200].content[text/event-stream]`. ## Changes * Changed `request.stream` on `chat.create()`. ## Breaking Changes * Removed `response.status[200].content[text/event-stream]` from `chat.create()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.17.0) ## api-client-go v0.15.0 API ClientsBreakingAction required Api-client-go v0.15.0 includes 1 breaking removal, 1 change. **Read more** ## Action Required * Update callers of `chat.create()` to stop using `response.status[200].content[text/event-stream]`. ## Changes * Changed `request.Request.Stream` on `chat.create()`. ## Breaking Changes * Removed `response.status[200].content[text/event-stream]` from `chat.create()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.15.0) September 1 2026 ## api-client-typescript v0.19.1 API Clients Api-client-typescript v0.19.1 includes 2 additions, 4 changes. **Read more** ## Changes * Added `error.authenticationSuggestions[].requestType.enum(sandboxEgress)` to `client.agents.runstream()`. * Added `error.authenticationSuggestions[].requestType.enum(sandboxEgress)` to `client.agents.run()`. * Changed `request.chatRequest.messages[].fragments[]` on `client.chat.create()`. * Changed `response.messages[].fragments[]` on `client.chat.create()`. * Changed `response.chatResult.chat.messages[].fragments[]` on `client.chat.retrieve()`. * Changed `request.chatRequest.messages[].fragments[]` on `client.chat.createstream()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.19.1) ## api-client-python v0.16.1 API Clients Api-client-python v0.16.1 includes 2 additions, 4 changes. **Read more** ## Changes * Added `error.authentication_suggestions[].request_type.enum(sandbox_egress)` to `client.agents.run_stream()`. * Added `error.authentication_suggestions[].request_type.enum(sandbox_egress)` to `client.agents.run()`. * Changed `request.messages[].fragments[]` on `client.chat.create()`. * Changed `response.messages[].fragments[]` on `client.chat.create()`. * Changed `response.chat_result.chat.messages[].fragments[]` on `client.chat.retrieve()`. * Changed `request.messages[].fragments[]` on `client.chat.create_stream()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.16.1) ## api-client-java v0.16.1 API Clients Api-client-java v0.16.1 includes 2 additions, 4 changes. **Read more** ## Changes * Added `error.authenticationSuggestions[].requestType.enum(sandboxEgress)` to `client.agents.runstream()`. * Added `error.authenticationSuggestions[].requestType.enum(sandboxEgress)` to `client.agents.run()`. * Changed `request.chatRequest.messages[].fragments[]` on `client.chat.create()`. * Changed `response.messages[].fragments[]` on `client.chat.create()`. * Changed `response.chatResult.chat.messages[].fragments[]` on `client.chat.retrieve()`. * Changed `request.chatRequest.messages[].fragments[]` on `client.chat.createstream()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.16.1) ## api-client-go v0.14.1 API Clients Api-client-go v0.14.1 includes 2 additions, 4 changes. **Read more** ## Changes * Added `error.AuthenticationSuggestions[].RequestType.Enum(sandboxEgress)` to `client.agents.runstream()`. * Added `error.AuthenticationSuggestions[].RequestType.Enum(sandboxEgress)` to `client.agents.run()`. * Changed `request.ChatRequest.Messages[].Fragments[]` on `client.chat.create()`. * Changed `response.Messages[].Fragments[]` on `client.chat.create()`. * Changed `response.ChatResult.Chat.Messages[].Fragments[]` on `client.chat.retrieve()`. * Changed `request.ChatRequest.Messages[].Fragments[]` on `client.chat.createstream()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.14.1) August 28 2026 ## glean-indexing-sdk v1.1.0 Glean Indexing SDK Glean-indexing-sdk v1.1.0: **deployment**: harden managed connector workflows. **Read more** ## Changes * **deployment**: harden managed connector workflows. * **release**: exclude uv build sentinel from payload. * **people**: preserve employee replacement compatibility. * **deployment**: enforce exact connector secret access. * **testing**: reject unbounded negative identity access. * **connectors**: preserve incremental and empty full semantics. * **cli**: honor deploy global options. * **observability**: restore connector lifecycle parity. * **release**: bind publish artifacts to tagged SHA. * **deploy**: sanitize derived connector names. * **deployment**: validate derived Kubernetes, GCP, and AWS resource names. * **connectors**: preserve identity uploads and options. * **gcp**: repair advertised cloud extra. * **deploy**: make artifact generation non-destructive. * **deploy**: synchronize container runtime IDs. * **deploy**: accept valid AWS account IDs. * **testing**: require explicit destructive opt-in for live runs. * **testing**: reject unsafe live identity mutations. * **testing**: reject live target changes after confirmation. * **testing**: use current source data for live runs. * **testing**: enforce the max-items contract. * Support connector factories in deployed runners. * Forward document\_batch\_size\_bytes to all connector base classes. * Bump js-yaml to 3.15.1 to resolve npm audit high-severity findings. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v1.1.0) * [PR #201](https://github.com/gleanwork/glean-indexing-sdk/pull/201) * [PR #200](https://github.com/gleanwork/glean-indexing-sdk/pull/200) * [PR #198](https://github.com/gleanwork/glean-indexing-sdk/pull/198) * [PR #197](https://github.com/gleanwork/glean-indexing-sdk/pull/197) * [PR #196](https://github.com/gleanwork/glean-indexing-sdk/pull/196) * [PR #195](https://github.com/gleanwork/glean-indexing-sdk/pull/195) * [PR #189](https://github.com/gleanwork/glean-indexing-sdk/pull/189) * [PR #192](https://github.com/gleanwork/glean-indexing-sdk/pull/192) * [PR #184](https://github.com/gleanwork/glean-indexing-sdk/pull/184) * [PR #188](https://github.com/gleanwork/glean-indexing-sdk/pull/188) * [PR #182](https://github.com/gleanwork/glean-indexing-sdk/pull/182) * [PR #185](https://github.com/gleanwork/glean-indexing-sdk/pull/185) * [PR #186](https://github.com/gleanwork/glean-indexing-sdk/pull/186) * [PR #187](https://github.com/gleanwork/glean-indexing-sdk/pull/187) * [PR #177](https://github.com/gleanwork/glean-indexing-sdk/pull/177) * [PR #176](https://github.com/gleanwork/glean-indexing-sdk/pull/176) * [PR #172](https://github.com/gleanwork/glean-indexing-sdk/pull/172) * [PR #175](https://github.com/gleanwork/glean-indexing-sdk/pull/175) * [PR #174](https://github.com/gleanwork/glean-indexing-sdk/pull/174) * [PR #171](https://github.com/gleanwork/glean-indexing-sdk/pull/171) * [PR #173](https://github.com/gleanwork/glean-indexing-sdk/pull/173) * [PR #169](https://github.com/gleanwork/glean-indexing-sdk/pull/169) * [PR #150](https://github.com/gleanwork/glean-indexing-sdk/pull/150) * [PR #147](https://github.com/gleanwork/glean-indexing-sdk/pull/147) August 27 2026 ## glean-indexing-sdk v1.0.0 Glean Indexing SDK Glean-indexing-sdk v1.0.0: **release**: exclude uv build sentinel from payload. **Read more** ## Changes * **cli**: add test, schema, and completion commands. * **cli**: add glean-idx run. * **cli**: ship connector validation as glean-idx validate. * **cli**: add the datasource command group. * **cli**: add the document command group. * **cli**: resolve connector projects and enforce preconditions. * **cli**: add the glean-idx foundation and doctor command. * Add configurable upload timeout to ConnectorOptions. * **release**: exclude uv build sentinel from payload. * **people**: preserve employee replacement compatibility. * **deployment**: enforce exact connector secret access. * **testing**: reject unbounded negative identity access. * **connectors**: preserve incremental and empty full semantics. * **cli**: honor deploy global options. * **observability**: restore connector lifecycle parity. * **release**: bind publish artifacts to tagged SHA. * **deploy**: sanitize derived connector names. * **deployment**: validate derived Kubernetes, GCP, and AWS resource names. * **connectors**: preserve identity uploads and options. * **gcp**: repair advertised cloud extra. * **deploy**: make artifact generation non-destructive. * **deploy**: synchronize container runtime IDs. * **deploy**: accept valid AWS account IDs. * **testing**: require explicit destructive opt-in for live runs. * **testing**: reject unsafe live identity mutations. * **testing**: reject live target changes after confirmation. * **testing**: use current source data for live runs. * **testing**: enforce the max-items contract. * Support connector factories in deployed runners. * Forward document\_batch\_size\_bytes to all connector base classes. * Bump js-yaml to 3.15.1 to resolve npm audit high-severity findings. * **deployment**: strip https\:// from OIDC issuer URL in AWS IRSA trust policy. * **cli**: move deploy under glean-idx. * Remove experimental worker module. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v1.0.0) * [PR #200](https://github.com/gleanwork/glean-indexing-sdk/pull/200) * [PR #198](https://github.com/gleanwork/glean-indexing-sdk/pull/198) * [PR #197](https://github.com/gleanwork/glean-indexing-sdk/pull/197) * [PR #196](https://github.com/gleanwork/glean-indexing-sdk/pull/196) * [PR #195](https://github.com/gleanwork/glean-indexing-sdk/pull/195) * [PR #189](https://github.com/gleanwork/glean-indexing-sdk/pull/189) * [PR #192](https://github.com/gleanwork/glean-indexing-sdk/pull/192) * [PR #184](https://github.com/gleanwork/glean-indexing-sdk/pull/184) * [PR #188](https://github.com/gleanwork/glean-indexing-sdk/pull/188) * [PR #182](https://github.com/gleanwork/glean-indexing-sdk/pull/182) * [PR #185](https://github.com/gleanwork/glean-indexing-sdk/pull/185) * [PR #186](https://github.com/gleanwork/glean-indexing-sdk/pull/186) * [PR #187](https://github.com/gleanwork/glean-indexing-sdk/pull/187) * [PR #177](https://github.com/gleanwork/glean-indexing-sdk/pull/177) * [PR #176](https://github.com/gleanwork/glean-indexing-sdk/pull/176) * [PR #172](https://github.com/gleanwork/glean-indexing-sdk/pull/172) * [PR #175](https://github.com/gleanwork/glean-indexing-sdk/pull/175) * [PR #174](https://github.com/gleanwork/glean-indexing-sdk/pull/174) * [PR #171](https://github.com/gleanwork/glean-indexing-sdk/pull/171) * [PR #173](https://github.com/gleanwork/glean-indexing-sdk/pull/173) * [PR #169](https://github.com/gleanwork/glean-indexing-sdk/pull/169) * [PR #150](https://github.com/gleanwork/glean-indexing-sdk/pull/150) * [PR #147](https://github.com/gleanwork/glean-indexing-sdk/pull/147) * [PR #132](https://github.com/gleanwork/glean-indexing-sdk/pull/132) * [PR #131](https://github.com/gleanwork/glean-indexing-sdk/pull/131) * [PR #130](https://github.com/gleanwork/glean-indexing-sdk/pull/130) * [PR #127](https://github.com/gleanwork/glean-indexing-sdk/pull/127) * [PR #126](https://github.com/gleanwork/glean-indexing-sdk/pull/126) * [PR #124](https://github.com/gleanwork/glean-indexing-sdk/pull/124) * [PR #123](https://github.com/gleanwork/glean-indexing-sdk/pull/123) * [PR #19](https://github.com/gleanwork/glean-indexing-sdk/pull/19) * [PR #134](https://github.com/gleanwork/glean-indexing-sdk/pull/134) * [PR #125](https://github.com/gleanwork/glean-indexing-sdk/pull/125) * [PR #52](https://github.com/gleanwork/glean-indexing-sdk/pull/52) August 21 2026 ## api-client-typescript v0.19.0 API Clients Api-client-typescript v0.19.0 includes 4 additions, 4 changes. **Read more** ## Changes * Added `request.chatRequest.messages[].citations[].sourceSkill` to `client.chat.create()`. * Added `response.messages[].citations[].sourceSkill` to `client.chat.create()`. * Added `response.chatResult.chat.messages[].citations[].sourceSkill` to `client.chat.retrieve()`. * Added `request.chatRequest.messages[].citations[].sourceSkill` to `client.chat.createstream()`. * Changed `request.input.union(Array)[].role` on `chat.create()`. * Changed `response` on `chat.create()`. * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.19.0) ## api-client-python v0.16.0 API Clients Api-client-python v0.16.0 includes 4 additions, 4 changes. **Read more** ## Changes * Added `request.messages[].citations[].source_skill` to `client.chat.create()`. * Added `response.messages[].citations[].source_skill` to `client.chat.create()`. * Added `response.chat_result.chat.messages[].citations[].source_skill` to `client.chat.retrieve()`. * Added `request.messages[].citations[].source_skill` to `client.chat.create_stream()`. * Changed `request.input.union(Array)[].role` on `chat.create()`. * Changed `response` on `chat.create()`. * Changed `request.categories[]` on `client.search.retrieve_feed()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.16.0) ## api-client-java v0.16.0 API Clients Api-client-java v0.16.0 includes 4 additions, 4 changes. **Read more** ## Changes * Added `request.chatRequest.messages[].citations[].sourceSkill` to `client.chat.create()`. * Added `response.messages[].citations[].sourceSkill` to `client.chat.create()`. * Added `response.chatResult.chat.messages[].citations[].sourceSkill` to `client.chat.retrieve()`. * Added `request.chatRequest.messages[].citations[].sourceSkill` to `client.chat.createstream()`. * Changed `request.input.union(Array)[].role` on `chat.create()`. * Changed `response` on `chat.create()`. * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.16.0) ## api-client-go v0.14.0 API Clients Api-client-go v0.14.0 includes 4 additions, 4 changes. **Read more** ## Changes * Added `request.ChatRequest.Messages[].Citations[].SourceSkill` to `client.chat.create()`. * Added `response.Messages[].Citations[].SourceSkill` to `client.chat.create()`. * Added `response.ChatResult.Chat.Messages[].Citations[].SourceSkill` to `client.chat.retrieve()`. * Added `request.ChatRequest.Messages[].Citations[].SourceSkill` to `client.chat.createstream()`. * Changed `request.Request.Input.union(Array)[].Role` on `chat.create()`. * Changed `response` on `chat.create()`. * Changed `request.FeedRequest.Categories[]` on `client.search.retrievefeed()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.14.0) August 20 2026 ## Platform Triggers API (Experimental) API The Platform Triggers API is now documented as an experimental release — subscribe to content events in Glean and receive them at your own endpoint as signed webhooks, instead of polling for changes. **Read more** ## Changes * Added endpoints: `GET /api/trigger-presets`, `GET /api/trigger-presets/{preset_id}`, `GET /api/trigger-presets/{preset_id}/input-values`. * Added endpoints: `POST /api/triggers`, `GET /api/triggers`, `GET /api/triggers/{trigger_id}`, `PATCH /api/triggers/{trigger_id}`, `DELETE /api/triggers/{trigger_id}`. * Added endpoints: `POST /api/trigger-presets/{preset_id}/events/search`, `POST /api/triggers/{trigger_id}/events/search` — preview what a preset would match, or search recent events a trigger's current configuration matches. * Triggers are created from **presets**: curated event definitions per datasource, each declaring the inputs it accepts, discoverable at runtime. * Events arrive as [Standard Webhooks](https://www.standardwebhooks.com), signed with HMAC-SHA256. The signing secret is returned once, at creation. Delivery is at least once, and `webhook-id` is stable across retries. * `delivery.auth` attaches a bearer token sent alongside the signature, not instead of it. * Events are scoped to what the subscribing user is permitted to see. ## Experimental These endpoints are experimental. Send `X-Glean-Include-Experimental: true` or they are not exposed. They may change or be removed without notice, and the standard API lifecycle guarantees do not apply. See [Experimental APIs](/experimental/overview.md). ## Documentation * [Triggers API overview](/api/platform-api/triggers-overview.md) * [List trigger presets](/api/platform-api/platform-trigger-presets-list.md) * [Create trigger](/api/platform-api/platform-triggers-create.md) August 5 2026 ## glean-indexing-sdk v1.0.0rc0 Glean Indexing SDK Glean-indexing-sdk v1.0.0rc0: **cli**: add test, schema, and completion commands. **Read more** ## Changes * **cli**: add test, schema, and completion commands. * **cli**: add glean-idx run. * **cli**: ship connector validation as glean-idx validate. * **cli**: add the datasource command group. * **cli**: add the document command group. * **cli**: resolve connector projects and enforce preconditions. * **cli**: add the glean-idx foundation and doctor command. * Add configurable upload timeout to ConnectorOptions. * Add ConnectorOptions for bulk indexing configuration. * **deployment**: strip https\:// from OIDC issuer URL in AWS IRSA trust policy. * Sync markdown doc with reformatted snippet import order. * Use attribute access in configure\_datasource() to avoid camelCase kwargs. * **cli**: move deploy under glean-idx. * Remove experimental worker module. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v1.0.0rc0) * [PR #132](https://github.com/gleanwork/glean-indexing-sdk/pull/132) * [PR #131](https://github.com/gleanwork/glean-indexing-sdk/pull/131) * [PR #130](https://github.com/gleanwork/glean-indexing-sdk/pull/130) * [PR #127](https://github.com/gleanwork/glean-indexing-sdk/pull/127) * [PR #126](https://github.com/gleanwork/glean-indexing-sdk/pull/126) * [PR #124](https://github.com/gleanwork/glean-indexing-sdk/pull/124) * [PR #123](https://github.com/gleanwork/glean-indexing-sdk/pull/123) * [PR #19](https://github.com/gleanwork/glean-indexing-sdk/pull/19) * [PR #134](https://github.com/gleanwork/glean-indexing-sdk/pull/134) * [PR #125](https://github.com/gleanwork/glean-indexing-sdk/pull/125) * [PR #52](https://github.com/gleanwork/glean-indexing-sdk/pull/52) August 4 2026 ## REST API: changes (endpoints) 2026-08-04 APINoteworthy 1 endpoint added. **Read more** ## Changes * Added endpoint: /tool-servers/{serverId}/tools ## Source * [open-api 0809530](https://github.com/gleanwork/open-api/commit/080953028c9e47551287142d11b77bb86d431b4c) * [open-api 0be0e04](https://github.com/gleanwork/open-api/commit/0be0e0473775c21f657b5b450ecfc4c9e5acfa02) * [open-api 18a683c](https://github.com/gleanwork/open-api/commit/18a683ca1e11151781288cf9be1d3edba125f9e9) * [open-api 1dcebdb](https://github.com/gleanwork/open-api/commit/1dcebdb0fd6465a22228976a2a34b5b2cdf0538a) * [open-api 3fdab96](https://github.com/gleanwork/open-api/commit/3fdab962b5825b7c0b902e0accbdcb38e8ed6b59) * [open-api 4690f7e](https://github.com/gleanwork/open-api/commit/4690f7e7dd919d8ed34d1f7b09f885839f4ade43) * [open-api 46a9f32](https://github.com/gleanwork/open-api/commit/46a9f32120bfd62f558b616d34c02f7c5128bf5d) * [open-api 4bcd05c](https://github.com/gleanwork/open-api/commit/4bcd05cb81511c66c6b6dc1c868fe8deef214a3c) August 3 2026 ## REST API: changes (endpoints) 2026-08-03 APINoteworthy 1 endpoint added. **Read more** ## Changes * Added endpoint: /agents/{agent\_id}/import ## Source * [open-api 05b91ed](https://github.com/gleanwork/open-api/commit/05b91ed3f5fe7e8e466282bc4d3310ce9f3f3ec3) * [open-api 3a0319b](https://github.com/gleanwork/open-api/commit/3a0319b8edeb91bb7df1e0a8d69c4d78c3e4356a) * [open-api 3d6799d](https://github.com/gleanwork/open-api/commit/3d6799d152c55521cb4b4cb6a552e7d9e45de190) * [open-api 654cceb](https://github.com/gleanwork/open-api/commit/654cceb01e5dabc4a050e06661f7e404869f5ce7) * [open-api 6614a94](https://github.com/gleanwork/open-api/commit/6614a94408401c45827c3f3fb42fc2f0cc17c5e5) * [open-api 7c1cbe9](https://github.com/gleanwork/open-api/commit/7c1cbe976d51476bc4710e80c7a72f51d868466a) * [open-api 892f265](https://github.com/gleanwork/open-api/commit/892f265f99e911faca69c14d7843b5b1041ce900) * [open-api 8d778d4](https://github.com/gleanwork/open-api/commit/8d778d470db9a68c490027c0b1f0206ec53c668d) July 29 2026 ## mcp-config v5.4.1 MCPAction required Mcp-config v5.4.1: `mcp-config-schema`: update documentation URLs for Antigravity, Gemini CLI, Goose, and Linear ([@glean-github-app-oauth\[bot\]](https://github.com/apps/glean-github-app-oauth)). **Read more** ## Action Required * Review OAuth configuration before upgrading. ## Changes * `mcp-config-schema`: update documentation URLs for Antigravity, Gemini CLI, Goose, and Linear ([@glean-github-app-oauth\[bot\]](https://github.com/apps/glean-github-app-oauth)). ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v5.4.1) * [PR #129](https://github.com/gleanwork/mcp-config/pull/129) July 21 2026 ## api-client-typescript v0.18.0 API Clients Api-client-typescript v0.18.0 includes 6 additions, 3 changes. **Read more** ## Changes * Added `request.chatRequest.messages[].fragments[].action.metadata.actionTypeSource` to `client.chat.create()`. * Added `response.messages[].fragments[].action.metadata.actionTypeSource` to `client.chat.create()`. * Added `response.chatResult.chat.messages[].fragments[].action.metadata.actionTypeSource` to `client.chat.retrieve()`. * Added `request.chatRequest.messages[].fragments[].action.metadata.actionTypeSource` to `client.chat.createstream()`. * Added `response.workflow.webhookUrl` to `client.agents.create()`. * Added `response.results[].primaryEntry.workflow.workflow.webhookUrl` to `client.search.retrievefeed()`. * Changed `request` on `client.governance.data.findings.create()`. * Changed `response` on `client.governance.data.findings.create()`. * Changed `response.exports[]` on `client.governance.data.findings.list()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.18.0) ## api-client-python v0.15.4 API Clients Api-client-python v0.15.4 includes 6 additions, 3 changes. **Read more** ## Changes * Added `request.messages[].fragments[].action.metadata.action_type_source` to `client.chat.create()`. * Added `response.messages[].fragments[].action.metadata.action_type_source` to `client.chat.create()`. * Added `response.chat_result.chat.messages[].fragments[].action.metadata.action_type_source` to `client.chat.retrieve()`. * Added `request.messages[].fragments[].action.metadata.action_type_source` to `client.chat.create_stream()`. * Added `response.workflow.webhook_url` to `client.agents.create()`. * Added `response.results[].primary_entry.workflow.workflow.webhook_url` to `client.search.retrieve_feed()`. * Changed `request` on `client.governance.data.findings.create()`. * Changed `response` on `client.governance.data.findings.create()`. * Changed `response.exports[]` on `client.governance.data.findings.list()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.15.4) ## api-client-java v0.14.4 API Clients Api-client-java v0.14.4 includes 6 additions, 3 changes. **Read more** ## Changes * Added `request.chatRequest.messages[].fragments[].action.metadata.actionTypeSource` to `client.chat.create()`. * Added `response.messages[].fragments[].action.metadata.actionTypeSource` to `client.chat.create()`. * Added `response.chatResult.chat.messages[].fragments[].action.metadata.actionTypeSource` to `client.chat.retrieve()`. * Added `request.chatRequest.messages[].fragments[].action.metadata.actionTypeSource` to `client.chat.createstream()`. * Added `response.workflow.webhookUrl` to `client.agents.create()`. * Added `response.results[].primaryEntry.workflow.workflow.webhookUrl` to `client.search.retrievefeed()`. * Changed `request` on `client.governance.data.findings.create()`. * Changed `response` on `client.governance.data.findings.create()`. * Changed `response.exports[]` on `client.governance.data.findings.list()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.14.4) ## api-client-go v0.13.4 API Clients Api-client-go v0.13.4 includes 6 additions, 3 changes. **Read more** ## Changes * Added `request.ChatRequest.Messages[].Fragments[].Action.Metadata.ActionTypeSource` to `client.chat.create()`. * Added `response.Messages[].Fragments[].Action.Metadata.ActionTypeSource` to `client.chat.create()`. * Added `response.ChatResult.Chat.Messages[].Fragments[].Action.Metadata.ActionTypeSource` to `client.chat.retrieve()`. * Added `request.ChatRequest.Messages[].Fragments[].Action.Metadata.ActionTypeSource` to `client.chat.createstream()`. * Added `response.Workflow.WebhookUrl` to `client.agents.create()`. * Added `response.Results[].PrimaryEntry.Workflow.Workflow.WebhookUrl` to `client.search.retrievefeed()`. * Changed `request.Request` on `client.governance.data.findings.create()`. * Changed `response` on `client.governance.data.findings.create()`. * Changed `response.Exports[]` on `client.governance.data.findings.list()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.13.4) July 20 2026 ## glean-agent-toolkit 0.7.0 Glean Agent Toolkit Glean-agent-toolkit 0.7.0: **tools**: add transport seam, typed search backend, result truncation, status-code errors. **Read more** ## Changes * **tools**: add transport seam, typed search backend, result truncation, status-code errors. * **chat**: read citations from fragments with legacy fallback. * **core**: ctx-aware as\_\*\_tool, registry collision warnings, retry unit fix. * **read\_document**: support renamed retrieve kwarg across glean-api-client versions. ## Source * [Release notes](https://github.com/gleanwork/glean-agent-toolkit/releases/tag/0.7.0) * [PR #87](https://github.com/gleanwork/glean-agent-toolkit/pull/87) * [PR #83](https://github.com/gleanwork/glean-agent-toolkit/pull/83) * [PR #84](https://github.com/gleanwork/glean-agent-toolkit/pull/84) * [PR #81](https://github.com/gleanwork/glean-agent-toolkit/pull/81) July 19 2026 ## glean-agent-toolkit 0.6.1 Glean Agent Toolkit Glean-agent-toolkit 0.6.1: **read\_document**: support renamed retrieve kwarg across glean-api-client versions. **Read more** ## Changes * **read\_document**: support renamed retrieve kwarg across glean-api-client versions. * **crewai**: pass args\_schema to BaseTool so the LLM sees real parameters. * **adk**: expose real typed signatures so declarations and invocation work. * **openai**: sanitize strict schemas and isolate per-tool conversion failures. * **adapters**: map anyOf/union and array item types correctly in get\_field\_type. * **langchain**: use StructuredTool so converted tools are invocable. * **tools**: stop closing shared Glean client on every tool call. ## Source * [Release notes](https://github.com/gleanwork/glean-agent-toolkit/releases/tag/0.6.1) * [PR #81](https://github.com/gleanwork/glean-agent-toolkit/pull/81) * [PR #73](https://github.com/gleanwork/glean-agent-toolkit/pull/73) * [PR #77](https://github.com/gleanwork/glean-agent-toolkit/pull/77) * [PR #76](https://github.com/gleanwork/glean-agent-toolkit/pull/76) * [PR #75](https://github.com/gleanwork/glean-agent-toolkit/pull/75) * [PR #74](https://github.com/gleanwork/glean-agent-toolkit/pull/74) * [PR #72](https://github.com/gleanwork/glean-agent-toolkit/pull/72) ## glean-agent-toolkit 0.6.0 Glean Agent ToolkitAction requiredDeprecated Glean-agent-toolkit 0.6.0: **crewai**: pass args\_schema to BaseTool so the LLM sees real parameters. **Read more** ## Action Required * Plan migration away from deprecated behavior. ## Changes * Add installable skills for SDK usage and tool building. * Search API alignment, chat tool, deprecation path, get\_tools, async support, import docs. * Injectable GleanContext replaces hidden api\_client() global (#62). * **deps**: add \[all] extra combining all framework adapters (CHK-001). * **crewai**: pass args\_schema to BaseTool so the LLM sees real parameters. * **adk**: expose real typed signatures so declarations and invocation work. * **openai**: sanitize strict schemas and isolate per-tool conversion failures. * **adapters**: map anyOf/union and array item types correctly in get\_field\_type. * **langchain**: use StructuredTool so converted tools are invocable. * **tools**: stop closing shared Glean client on every tool call. * Correct web search tool name from 'Web Browser' to 'Gemini Web Search'. * Resolve remaining P2 eval items (CHK-111, CHK-115, CHK-118, CHK-119). * Resolve eval checklist items — dedup, dead code, error handling, imports. * Namespace tool names and optimize descriptions for LLM consumption. * Structured error results and consistent adapter return types. * Depend on langchain-core instead of langchain to support LangGraph 1.x. * Regenerate lockfile. * Align publish workflow tag trigger with commitizen tag format. * Use is not None checks in read\_document validation. * Export Registry from top-level package. * Remove pydantic BaseModel from adapters public API. * Preserve float values in retry backoff config (CHK-002). * **release**: correct broken version\_files path in .cz.toml (CHK-006). ## Source * [Release notes](https://github.com/gleanwork/glean-agent-toolkit/releases/tag/0.6.0) * [PR #73](https://github.com/gleanwork/glean-agent-toolkit/pull/73) * [PR #77](https://github.com/gleanwork/glean-agent-toolkit/pull/77) * [PR #76](https://github.com/gleanwork/glean-agent-toolkit/pull/76) * [PR #75](https://github.com/gleanwork/glean-agent-toolkit/pull/75) * [PR #74](https://github.com/gleanwork/glean-agent-toolkit/pull/74) * [PR #72](https://github.com/gleanwork/glean-agent-toolkit/pull/72) * [PR #70](https://github.com/gleanwork/glean-agent-toolkit/pull/70) * [PR #66](https://github.com/gleanwork/glean-agent-toolkit/pull/66) * [PR #65](https://github.com/gleanwork/glean-agent-toolkit/pull/65) * [PR #27](https://github.com/gleanwork/glean-agent-toolkit/pull/27) * [PR #71](https://github.com/gleanwork/glean-agent-toolkit/pull/71) * [PR #68](https://github.com/gleanwork/glean-agent-toolkit/pull/68) * [PR #67](https://github.com/gleanwork/glean-agent-toolkit/pull/67) * [PR #58](https://github.com/gleanwork/glean-agent-toolkit/pull/58) * [PR #55](https://github.com/gleanwork/glean-agent-toolkit/pull/55) * [PR #54](https://github.com/gleanwork/glean-agent-toolkit/pull/54) * [PR #39](https://github.com/gleanwork/glean-agent-toolkit/pull/39) * [PR #37](https://github.com/gleanwork/glean-agent-toolkit/pull/37) * [PR #36](https://github.com/gleanwork/glean-agent-toolkit/pull/36) * [PR #35](https://github.com/gleanwork/glean-agent-toolkit/pull/35) * [PR #31](https://github.com/gleanwork/glean-agent-toolkit/pull/31) July 15 2026 ## REST API: changes (endpoints) 2026-07-15 APINoteworthy 2 endpoints added. **Read more** ## Changes * Added endpoint: /rest/api/index/submissions/{datasourceInstance}/{type} * Added endpoint: /submissions/{datasourceInstance}/{type} ## Source * [open-api 0014369](https://github.com/gleanwork/open-api/commit/0014369d53ab6aeab9d28a4d3a24dea2361c43cd) * [open-api 0dce218](https://github.com/gleanwork/open-api/commit/0dce2185d014565fa994ea8b485b2c354632bc35) * [open-api 0dfbcff](https://github.com/gleanwork/open-api/commit/0dfbcffd5e93be2858b792645a4ad878be2c6c80) * [open-api 2d5869e](https://github.com/gleanwork/open-api/commit/2d5869e5349aa2221f85aa1b4e8138acc76e02f2) * [open-api 2da199e](https://github.com/gleanwork/open-api/commit/2da199e3d09b50fb6e535ce8d682e8faea0d5c4f) * [open-api 2e06973](https://github.com/gleanwork/open-api/commit/2e069735ae7931e10a7fc35947465d1995fcdb48) * [open-api 32e7a51](https://github.com/gleanwork/open-api/commit/32e7a5113a574eea522faa51c2d948e14af0c650) * [open-api 438762b](https://github.com/gleanwork/open-api/commit/438762b2397cac4b50c63dba42784619bca9279c) ## mcp-config v5.4.0 MCP Mcp-config v5.4.0: `mcp-config-glean`, `mcp-config-schema`: add managed setup URLs. **Read more** ## Changes * `mcp-config-glean`, `mcp-config-schema`: add managed setup URLs. * `mcp-config-schema`: add Cursor Team MCP Servers client. ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v5.4.0) * [PR #126](https://github.com/gleanwork/mcp-config/pull/126) * [PR #125](https://github.com/gleanwork/mcp-config/pull/125) July 14 2026 ## mcp-config v5.3.0 MCP Mcp-config v5.3.0: `mcp-config-schema`: add Copilot Studio, Gemini Enterprise, and LibreChat clients. **Read more** ## Changes * `mcp-config-schema`: add Copilot Studio, Gemini Enterprise, and LibreChat clients. ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v5.3.0) * [PR #124](https://github.com/gleanwork/mcp-config/pull/124) July 10 2026 ## api-client-typescript v0.17.3 API Clients Api-client-typescript v0.17.3 includes 4 changes. **Read more** ## Changes * Changed `request.chatRequest.messages[].fragments[]` on `client.chat.create()`. * Changed `response.messages[].fragments[]` on `client.chat.create()`. * Changed `response.chatResult.chat.messages[].fragments[]` on `client.chat.retrieve()`. * Changed `request.chatRequest.messages[].fragments[]` on `client.chat.createstream()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.17.3) ## api-client-python v0.15.3 API Clients Api-client-python v0.15.3 includes 4 changes. **Read more** ## Changes * Changed `request.messages[].fragments[]` on `client.chat.create()`. * Changed `response.messages[].fragments[]` on `client.chat.create()`. * Changed `response.chat_result.chat.messages[].fragments[]` on `client.chat.retrieve()`. * Changed `request.messages[].fragments[]` on `client.chat.create_stream()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.15.3) ## api-client-java v0.14.3 API Clients Api-client-java v0.14.3 includes 4 changes. **Read more** ## Changes * Changed `request.chatRequest.messages[].fragments[]` on `client.chat.create()`. * Changed `response.messages[].fragments[]` on `client.chat.create()`. * Changed `response.chatResult.chat.messages[].fragments[]` on `client.chat.retrieve()`. * Changed `request.chatRequest.messages[].fragments[]` on `client.chat.createstream()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.14.3) ## api-client-go v0.13.3 API Clients Api-client-go v0.13.3 includes 4 changes. **Read more** ## Changes * Changed `request.ChatRequest.Messages[].Fragments[]` on `client.chat.create()`. * Changed `response.Messages[].Fragments[]` on `client.chat.create()`. * Changed `response.ChatResult.Chat.Messages[].Fragments[]` on `client.chat.retrieve()`. * Changed `request.ChatRequest.Messages[].Fragments[]` on `client.chat.createstream()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.13.3) July 8 2026 ## api-client-typescript v0.17.2 API Clients Api-client-typescript v0.17.2 includes 1 addition, 1 change. **Read more** ## Changes * Added `request.feedRequest.categories[].enum(adminHealthCenter)` to `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.17.2) ## api-client-python v0.15.2 API Clients Api-client-python v0.15.2 includes 1 addition, 1 change. **Read more** ## Changes * Added `request.categories[].enum(admin_health_center)` to `client.search.retrieve_feed()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.15.2) ## api-client-java v0.14.2 API Clients Api-client-java v0.14.2 includes 1 addition, 1 change. **Read more** ## Changes * Added `request.feedRequest.categories[].enum(adminHealthCenter)` to `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.14.2) ## api-client-go v0.13.2 API Clients Api-client-go v0.13.2 includes 1 addition, 1 change. **Read more** ## Changes * Added `request.FeedRequest.Categories[].Enum(adminHealthCenter)` to `client.search.retrievefeed()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.13.2) July 6 2026 ## REST API: changes (endpoints) 2026-07-06 APINoteworthy 1 endpoint added. **Read more** ## Changes * Added endpoint: /tool-servers/{serverId}/auth ## Source * [open-api 4a21fa5](https://github.com/gleanwork/open-api/commit/4a21fa5af798541d971684d6553cac6518076572) * [open-api 4fe01f0](https://github.com/gleanwork/open-api/commit/4fe01f099b6b30460fd3c6cd2ed47cb4a77797e1) * [open-api 566e3d3](https://github.com/gleanwork/open-api/commit/566e3d392ace2cb8f2cd9a6fd99c9f47b1fee05a) * [open-api 6154b37](https://github.com/gleanwork/open-api/commit/6154b37644c41cc06ec7707d153e0babaa182692) * [open-api 66b0eaf](https://github.com/gleanwork/open-api/commit/66b0eaff65a1685a2aac683388743350dc8b82cc) * [open-api 7163488](https://github.com/gleanwork/open-api/commit/71634881068d89d4bf7db5d0464d083bad3570c7) * [open-api 78aad8c](https://github.com/gleanwork/open-api/commit/78aad8ce6c1ac5d1bb48377182f00e85bb723591) * [open-api 872c2b5](https://github.com/gleanwork/open-api/commit/872c2b51976682d881d04d05a90d7df93c20883a) ## api-client-typescript v0.17.1 API Clients Api-client-typescript v0.17.1 includes 3 additions, 55 changes. **Read more** ## Changes * Added `error.code.enum(requestTooLarge)` to `agents.get()`. * Added `error.code.enum(requestTooLarge)` to `agents.getschemas()`. * Added `request.feedRequest.categories[].enum(podcast)` to `client.search.retrievefeed()`. * Changed `error` on `agents.search()`. * Changed `error` on `agents.createrun()`. * Changed `error` on `search.query()`. * Changed `request.createAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[]` on `client.announcements.create()`. * Changed `response` on `client.announcements.create()`. * Changed `request.updateAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[]` on `client.announcements.update()`. * Changed `response` on `client.announcements.update()`. * Changed `request.createAnswerRequest.data.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.answers.create()`. * Changed `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.answers.create()`. * Changed `request.editAnswerRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.answers.update()`. * Changed `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.answers.update()`. * Changed `response.answerResult` on `client.answers.retrieve()`. * Changed `response.answerResults[]` on `client.answers.list()`. * Changed `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.chat.create()`. * Changed `response.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.chat.create()`. * Changed `response.chatResult.chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.chat.retrieve()`. * Changed `response.chatResults[].chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.chat.list()`. * Changed `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.chat.createstream()`. * Changed `response.workflow.author.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.agents.create()`. * Changed `response.collection` on `client.collections.additems()`. * Changed `request.createCollectionRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.collections.create()`. * Changed `response.union(class (0)).collection` on `client.collections.create()`. * Changed `response.collection` on `client.collections.deleteitem()`. * Changed `request.editCollectionRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.collections.update()`. * Changed `response` on `client.collections.update()`. * Changed `response.collection` on `client.collections.updateitem()`. * Changed `response.collection` on `client.collections.retrieve()`. * Changed `response.collections[]` on `client.collections.list()`. * Changed `response.documents.Map.union(Document).metadata.author.relatedDocuments[].results[]` on `client.documents.retrieve()`. * Changed `response.documents[].metadata.author.relatedDocuments[].results[]` on `client.documents.retrievebyfacets()`. * Changed `response.gleanAssist.activityInsights[].user.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.insights.retrieve()`. * Changed `response.searchResponse.results[].structuredResults[].document.metadata` on `client.messages.retrieve()`. * Changed `response` on `client.pins.update()`. * Changed `response.pin` on `client.pins.retrieve()`. * Changed `response.pins[]` on `client.pins.list()`. * Changed `response` on `client.pins.create()`. * Changed `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.search.queryasadmin()`. * Changed `response.results[].structuredResults[].document.metadata` on `client.search.queryasadmin()`. * Changed `response.results[].document.metadata.author.relatedDocuments[].results[]` on `client.search.autocomplete()`. * Changed `response.results[]` on `client.search.retrievefeed()`. * Changed `request.recommendationsRequest.sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.search.recommendations()`. * Changed `response.results[].structuredResults[].document.metadata` on `client.search.recommendations()`. * Changed `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.search.query()`. * Changed `response.results[].structuredResults[].document.metadata` on `client.search.query()`. * Changed `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.entities.list()`. * Changed `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.entities.readpeople()`. * Changed `request.createShortcutRequest.data.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.shortcuts.create()`. * Changed `response.shortcut` on `client.shortcuts.create()`. * Changed `response.shortcut` on `client.shortcuts.retrieve()`. * Changed `response.shortcuts[]` on `client.shortcuts.list()`. * Changed `request.updateShortcutRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.shortcuts.update()`. * Changed `response.shortcut` on `client.shortcuts.update()`. * Changed `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.verification.addreminder()`. * Changed `response.documents[].metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.verification.list()`. * Changed `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.17.1) ## api-client-python v0.15.1 API Clients Api-client-python v0.15.1 includes 3 additions, 55 changes. **Read more** ## Changes * Added `error.code.enum(request_too_large)` to `agents.get()`. * Added `error.code.enum(request_too_large)` to `agents.get_schemas()`. * Added `request.categories[].enum(podcast)` to `client.search.retrieve_feed()`. * Changed `error` on `agents.search()`. * Changed `error` on `agents.create_run()`. * Changed `error` on `search.query()`. * Changed `request.body.structured_list[].document.metadata.author.related_documents[].results[]` on `client.announcements.create()`. * Changed `response` on `client.announcements.create()`. * Changed `request.body.structured_list[].document.metadata.author.related_documents[].results[]` on `client.announcements.update()`. * Changed `response` on `client.announcements.update()`. * Changed `request.data.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata` on `client.answers.create()`. * Changed `response.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata` on `client.answers.create()`. * Changed `request.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata` on `client.answers.update()`. * Changed `response.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata` on `client.answers.update()`. * Changed `response.answer_result` on `client.answers.retrieve()`. * Changed `response.answer_results[]` on `client.answers.list()`. * Changed `request.messages[].citations[].source_document.metadata.author.related_documents[].results[]` on `client.chat.create()`. * Changed `response.messages[].citations[].source_document.metadata.author.related_documents[].results[]` on `client.chat.create()`. * Changed `response.chat_result.chat.created_by.related_documents[].query_suggestion.ranges[].document.metadata` on `client.chat.retrieve()`. * Changed `response.chat_results[].chat.created_by.related_documents[].query_suggestion.ranges[].document.metadata` on `client.chat.list()`. * Changed `request.messages[].citations[].source_document.metadata.author.related_documents[].results[]` on `client.chat.create_stream()`. * Changed `response.workflow.author.related_documents[].query_suggestion.ranges[].document.metadata` on `client.agents.create()`. * Changed `response.collection` on `client.collections.add_items()`. * Changed `request.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata` on `client.collections.create()`. * Changed `response.union(class (0)).collection` on `client.collections.create()`. * Changed `response.collection` on `client.collections.delete_item()`. * Changed `request.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata` on `client.collections.update()`. * Changed `response` on `client.collections.update()`. * Changed `response.collection` on `client.collections.update_item()`. * Changed `response.collection` on `client.collections.retrieve()`. * Changed `response.collections[]` on `client.collections.list()`. * Changed `response.documents.Map.union(Document).metadata.author.related_documents[].results[]` on `client.documents.retrieve()`. * Changed `response.documents[].metadata.author.related_documents[].results[]` on `client.documents.retrieve_by_facets()`. * Changed `response.glean_assist.activity_insights[].user.related_documents[].query_suggestion.ranges[].document.metadata` on `client.insights.retrieve()`. * Changed `response.search_response.results[].structured_results[].document.metadata` on `client.messages.retrieve()`. * Changed `response` on `client.pins.update()`. * Changed `response.pin` on `client.pins.retrieve()`. * Changed `response.pins[]` on `client.pins.list()`. * Changed `response` on `client.pins.create()`. * Changed `request.source_document.metadata.author.related_documents[].results[]` on `client.search.query_as_admin()`. * Changed `response.results[].structured_results[].document.metadata` on `client.search.query_as_admin()`. * Changed `response.results[].document.metadata.author.related_documents[].results[]` on `client.search.autocomplete()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. * Changed `request.source_document.metadata.author.related_documents[].results[]` on `client.search.recommendations()`. * Changed `response.results[].structured_results[].document.metadata` on `client.search.recommendations()`. * Changed `request.source_document.metadata.author.related_documents[].results[]` on `client.search.query()`. * Changed `response.results[].structured_results[].document.metadata` on `client.search.query()`. * Changed `response.results[].related_documents[].query_suggestion.ranges[].document.metadata` on `client.entities.list()`. * Changed `response.results[].related_documents[].query_suggestion.ranges[].document.metadata` on `client.entities.read_people()`. * Changed `request.data.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata` on `client.shortcuts.create()`. * Changed `response.shortcut` on `client.shortcuts.create()`. * Changed `response.shortcut` on `client.shortcuts.retrieve()`. * Changed `response.shortcuts[]` on `client.shortcuts.list()`. * Changed `request.added_roles[].person.related_documents[].query_suggestion.ranges[].document.metadata` on `client.shortcuts.update()`. * Changed `response.shortcut` on `client.shortcuts.update()`. * Changed `response.metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata` on `client.verification.add_reminder()`. * Changed `response.documents[].metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata` on `client.verification.list()`. * Changed `response.metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.15.1) ## api-client-java v0.14.1 API Clients Api-client-java v0.14.1 includes 3 additions, 55 changes. **Read more** ## Changes * Added `error.code.enum(requestTooLarge)` to `agents.get()`. * Added `error.code.enum(requestTooLarge)` to `agents.getschemas()`. * Added `request.feedRequest.categories[].enum(podcast)` to `client.search.retrievefeed()`. * Changed `error` on `agents.search()`. * Changed `error` on `agents.createrun()`. * Changed `error` on `search.query()`. * Changed `request.createAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[]` on `client.announcements.create()`. * Changed `response` on `client.announcements.create()`. * Changed `request.updateAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[]` on `client.announcements.update()`. * Changed `response` on `client.announcements.update()`. * Changed `request.createAnswerRequest.data.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.answers.create()`. * Changed `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.answers.create()`. * Changed `request.editAnswerRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.answers.update()`. * Changed `response.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.answers.update()`. * Changed `response.answerResult` on `client.answers.retrieve()`. * Changed `response.answerResults[]` on `client.answers.list()`. * Changed `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.chat.create()`. * Changed `response.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.chat.create()`. * Changed `response.chatResult.chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.chat.retrieve()`. * Changed `response.chatResults[].chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.chat.list()`. * Changed `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.chat.createstream()`. * Changed `response.workflow.author.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.agents.create()`. * Changed `response.collection` on `client.collections.additems()`. * Changed `request.createCollectionRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.collections.create()`. * Changed `response.union(class (0)).collection` on `client.collections.create()`. * Changed `response.collection` on `client.collections.deleteitem()`. * Changed `request.editCollectionRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.collections.update()`. * Changed `response` on `client.collections.update()`. * Changed `response.collection` on `client.collections.updateitem()`. * Changed `response.collection` on `client.collections.retrieve()`. * Changed `response.collections[]` on `client.collections.list()`. * Changed `response.documents.Map.union(Document).metadata.author.relatedDocuments[].results[]` on `client.documents.retrieve()`. * Changed `response.documents[].metadata.author.relatedDocuments[].results[]` on `client.documents.retrievebyfacets()`. * Changed `response.gleanAssist.activityInsights[].user.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.insights.retrieve()`. * Changed `response.searchResponse.results[].structuredResults[].document.metadata` on `client.messages.retrieve()`. * Changed `response` on `client.pins.update()`. * Changed `response.pin` on `client.pins.retrieve()`. * Changed `response.pins[]` on `client.pins.list()`. * Changed `response` on `client.pins.create()`. * Changed `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.search.queryasadmin()`. * Changed `response.results[].structuredResults[].document.metadata` on `client.search.queryasadmin()`. * Changed `response.results[].document.metadata.author.relatedDocuments[].results[]` on `client.search.autocomplete()`. * Changed `response.results[]` on `client.search.retrievefeed()`. * Changed `request.recommendationsRequest.sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.search.recommendations()`. * Changed `response.results[].structuredResults[].document.metadata` on `client.search.recommendations()`. * Changed `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[]` on `client.search.query()`. * Changed `response.results[].structuredResults[].document.metadata` on `client.search.query()`. * Changed `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.entities.list()`. * Changed `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.entities.readpeople()`. * Changed `request.createShortcutRequest.data.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.shortcuts.create()`. * Changed `response.shortcut` on `client.shortcuts.create()`. * Changed `response.shortcut` on `client.shortcuts.retrieve()`. * Changed `response.shortcuts[]` on `client.shortcuts.list()`. * Changed `request.updateShortcutRequest.addedRoles[].person.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.shortcuts.update()`. * Changed `response.shortcut` on `client.shortcuts.update()`. * Changed `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.verification.addreminder()`. * Changed `response.documents[].metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.verification.list()`. * Changed `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.14.1) ## api-client-go v0.13.1 API Clients Api-client-go v0.13.1 includes 3 additions, 55 changes. **Read more** ## Changes * Added `error.Code.Enum(requestTooLarge)` to `agents.get()`. * Added `error.Code.Enum(requestTooLarge)` to `agents.getschemas()`. * Added `request.FeedRequest.Categories[].Enum(podcast)` to `client.search.retrievefeed()`. * Changed `error` on `agents.search()`. * Changed `error` on `agents.createrun()`. * Changed `error` on `search.query()`. * Changed `request.CreateAnnouncementRequest.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[]` on `client.announcements.create()`. * Changed `response` on `client.announcements.create()`. * Changed `request.UpdateAnnouncementRequest.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[]` on `client.announcements.update()`. * Changed `response` on `client.announcements.update()`. * Changed `request.CreateAnswerRequest.Data.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.answers.create()`. * Changed `response.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.answers.create()`. * Changed `request.EditAnswerRequest.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.answers.update()`. * Changed `response.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.answers.update()`. * Changed `response.AnswerResult` on `client.answers.retrieve()`. * Changed `response.AnswerResults[]` on `client.answers.list()`. * Changed `request.ChatRequest.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[]` on `client.chat.create()`. * Changed `response.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[]` on `client.chat.create()`. * Changed `response.ChatResult.Chat.CreatedBy.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.chat.retrieve()`. * Changed `response.ChatResults[].Chat.CreatedBy.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.chat.list()`. * Changed `request.ChatRequest.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[]` on `client.chat.createstream()`. * Changed `response.Workflow.Author.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.agents.create()`. * Changed `response.Collection` on `client.collections.additems()`. * Changed `request.CreateCollectionRequest.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.collections.create()`. * Changed `response.union(class (0)).Collection` on `client.collections.create()`. * Changed `response.Collection` on `client.collections.deleteitem()`. * Changed `request.EditCollectionRequest.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.collections.update()`. * Changed `response` on `client.collections.update()`. * Changed `response.Collection` on `client.collections.updateitem()`. * Changed `response.Collection` on `client.collections.retrieve()`. * Changed `response.Collections[]` on `client.collections.list()`. * Changed `response.Documents.Map.union(Document).Metadata.Author.RelatedDocuments[].Results[]` on `client.documents.retrieve()`. * Changed `response.Documents[].Metadata.Author.RelatedDocuments[].Results[]` on `client.documents.retrievebyfacets()`. * Changed `response.GleanAssist.ActivityInsights[].User.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.insights.retrieve()`. * Changed `response.SearchResponse.Results[].StructuredResults[].Document.Metadata` on `client.messages.retrieve()`. * Changed `response` on `client.pins.update()`. * Changed `response.Pin` on `client.pins.retrieve()`. * Changed `response.Pins[]` on `client.pins.list()`. * Changed `response` on `client.pins.create()`. * Changed `request.SearchRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[]` on `client.search.queryasadmin()`. * Changed `response.Results[].StructuredResults[].Document.Metadata` on `client.search.queryasadmin()`. * Changed `response.Results[].Document.Metadata.Author.RelatedDocuments[].Results[]` on `client.search.autocomplete()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. * Changed `request.RecommendationsRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[]` on `client.search.recommendations()`. * Changed `response.Results[].StructuredResults[].Document.Metadata` on `client.search.recommendations()`. * Changed `request.SearchRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[]` on `client.search.query()`. * Changed `response.Results[].StructuredResults[].Document.Metadata` on `client.search.query()`. * Changed `response.Results[].RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.entities.list()`. * Changed `response.Results[].RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.entities.readpeople()`. * Changed `request.CreateShortcutRequest.Data.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.shortcuts.create()`. * Changed `response.Shortcut` on `client.shortcuts.create()`. * Changed `response.Shortcut` on `client.shortcuts.retrieve()`. * Changed `response.Shortcuts[]` on `client.shortcuts.list()`. * Changed `request.UpdateShortcutRequest.AddedRoles[].Person.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.shortcuts.update()`. * Changed `response.Shortcut` on `client.shortcuts.update()`. * Changed `response.Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.verification.addreminder()`. * Changed `response.Documents[].Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.verification.list()`. * Changed `response.Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.13.1) June 30 2026 ## configure-mcp-server v3.2.1 MCP Configure-mcp-server v3.2.1: Inline mcp-server-utils utilities; drop the dependency. **Read more** ## Changes * Inline mcp-server-utils utilities; drop the dependency. ## Source * [Release notes](https://github.com/gleanwork/configure-mcp-server/releases/tag/v3.2.1) * [PR #84](https://github.com/gleanwork/configure-mcp-server/pull/84) June 29 2026 ## api-client-typescript v0.16.0 API Clients Api-client-typescript v0.16.0 includes 57 additions, 1 change. **Read more** ## Changes * Added `request.createAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.announcements.create()`. * Added `response.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.announcements.create()`. * Added `request.updateAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.announcements.update()`. * Added `response.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.announcements.update()`. * Added `request.createAnswerRequest.data.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.create()`. * Added `response.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.create()`. * Added `request.editAnswerRequest.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.update()`. * Added `response.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.update()`. * Added `response.answerResult.answer.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.retrieve()`. * Added `response.answerResults[].answer.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.list()`. * Added `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.chat.create()`. * Added `response.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.chat.create()`. * Added `response.chatResult.chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.chat.retrieve()`. * Added `response.chatResults[].chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.chat.list()`. * Added `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.chat.createstream()`. * Added `response.collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.additems()`. * Added `request.createCollectionRequest.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.create()`. * Added `response.union(class (0)).collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.create()`. * Added `response.collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.deleteitem()`. * Added `request.editCollectionRequest.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.update()`. * Added `response.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.update()`. * Added `response.collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.updateitem()`. * Added `response.collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.retrieve()`. * Added `response.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.collections.list()`. * Added `response.documents.Map.union(Document).metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.documents.retrieve()`. * Added `response.documents[].metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.documents.retrievebyfacets()`. * Added `response.gleanAssist.activityInsights[].user.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.insights.retrieve()`. * Added `response.searchResponse.results[].structuredResults[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.messages.retrieve()`. * Added `response.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.pins.update()`. * Added `response.pin.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.pins.retrieve()`. * Added `response.pins[].attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.pins.list()`. * Added `response.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.pins.create()`. * Added `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.search.queryasadmin()`. * Added `response.results[].structuredResults[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.search.queryasadmin()`. * Added `response.results[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.search.autocomplete()`. * Added `request.feedRequest.categories[].enum(cardStackPromo)` to `client.search.retrievefeed()`. * Added `request.recommendationsRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.search.recommendations()`. * Added `response.results[].structuredResults[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.search.recommendations()`. * Added `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.search.query()`. * Added `response.results[].structuredResults[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.search.query()`. * Added `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.entities.list()`. * Added `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.entities.readpeople()`. * Added `request.createShortcutRequest.data.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.retrieve()`. * Added `response.shortcuts[].addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.list()`. * Added `request.updateShortcutRequest.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.update()`. * Added `response.shortcut.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.update()`. * Added `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.verification.addreminder()`. * Added `response.documents[].metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.verification.list()`. * Added `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.verification.verify()`. * Added `response.report.config.allowlistOptions.regexes` to `client.governance.data.policies.retrieve()`. * Added `request.updateDlpReportRequest.config.allowlistOptions.regexes` to `client.governance.data.policies.update()`. * Added `response.reports[].config.allowlistOptions.regexes` to `client.governance.data.policies.list()`. * Added `request.config.allowlistOptions.regexes` to `client.governance.data.policies.create()`. * Added `response.report.config.allowlistOptions.regexes` to `client.governance.data.policies.create()`. * Added `request.config.allowlistOptions.regexes` to `client.governance.data.reports.create()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.16.0) ## api-client-python v0.14.0 API Clients Api-client-python v0.14.0 includes 56 additions, 2 changes. **Read more** ## Changes * Added `request.messages[].citations[].source_document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.chat.create()`. * Added `response.attribution.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.pins.update()`. * Added `request.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.announcements.update()`. * Added `response.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.announcements.update()`. * Added `request.data.added_roles[].group.type.enum(collection_audience)` to `client.answers.create()`. * Added `response.added_roles[].group.type.enum(collection_audience)` to `client.answers.create()`. * Added `request.added_roles[].group.type.enum(collection_audience)` to `client.answers.update()`. * Added `response.added_roles[].group.type.enum(collection_audience)` to `client.answers.update()`. * Added `response.answer_result.answer.added_roles[].group.type.enum(collection_audience)` to `client.answers.retrieve()`. * Added `response.answer_results[].answer.added_roles[].group.type.enum(collection_audience)` to `client.answers.list()`. * Added `request.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.announcements.create()`. * Added `response.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.announcements.create()`. * Added `response.chat_result.chat.created_by.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.chat.retrieve()`. * Added `response.chat_results[].chat.created_by.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.chat.list()`. * Added `request.messages[].citations[].source_document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.chat.create_stream()`. * Added `response.collection.added_roles[].group.type.enum(collection_audience)` to `client.collections.add_items()`. * Added `request.added_roles[].group.type.enum(collection_audience)` to `client.collections.create()`. * Added `response.union(class (0)).collection.added_roles[].group.type.enum(collection_audience)` to `client.collections.create()`. * Added `response.collection.added_roles[].group.type.enum(collection_audience)` to `client.collections.delete_item()`. * Added `request.added_roles[].group.type.enum(collection_audience)` to `client.collections.update()`. * Added `response.added_roles[].group.type.enum(collection_audience)` to `client.collections.update()`. * Added `response.collection.added_roles[].group.type.enum(collection_audience)` to `client.collections.update_item()`. * Added `response.collection.added_roles[].group.type.enum(collection_audience)` to `client.collections.retrieve()`. * Added `response.collections[].added_roles[].group.type.enum(collection_audience)` to `client.collections.list()`. * Added `response.documents.Map.union(Document).metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.documents.retrieve()`. * Added `response.documents[].metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.documents.retrieve_by_facets()`. * Added `response.glean_assist.activity_insights[].user.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.insights.retrieve()`. * Added `response.pin.attribution.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.pins.retrieve()`. * Added `response.search_response.results[].structured_results[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.messages.retrieve()`. * Added `response.pins[].attribution.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.pins.list()`. * Added `response.attribution.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.pins.create()`. * Added `request.source_document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.search.query_as_admin()`. * Added `response.results[].structured_results[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.search.query_as_admin()`. * Added `response.results[].document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.search.autocomplete()`. * Added `request.categories[].enum(card_stack_promo)` to `client.search.retrieve_feed()`. * Added `request.source_document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.search.recommendations()`. * Added `response.results[].structured_results[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.search.recommendations()`. * Added `request.source_document.metadata.author.related_documents[].results[].structured_results[].custom_entity.roles[].group.type.enum(collection_audience)` to `client.search.query()`. * Added `response.results[].structured_results[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.search.query()`. * Added `response.results[].related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.entities.list()`. * Added `response.results[].related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.entities.read_people()`. * Added `request.data.added_roles[].group.type.enum(collection_audience)` to `client.shortcuts.create()`. * Added `response.shortcut.added_roles[].group.type.enum(collection_audience)` to `client.shortcuts.create()`. * Added `response.shortcut.added_roles[].group.type.enum(collection_audience)` to `client.shortcuts.retrieve()`. * Added `response.shortcuts[].added_roles[].group.type.enum(collection_audience)` to `client.shortcuts.list()`. * Added `request.added_roles[].group.type.enum(collection_audience)` to `client.shortcuts.update()`. * Added `response.shortcut.added_roles[].group.type.enum(collection_audience)` to `client.shortcuts.update()`. * Added `response.metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.verification.add_reminder()`. * Added `response.documents[].metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.verification.list()`. * Added `response.metadata.last_verifier.related_documents[].query_suggestion.ranges[].document.metadata.collections[].added_roles[].group.type.enum(collection_audience)` to `client.verification.verify()`. * Added `response.report.config.allowlist_options.regexes` to `client.governance.data.policies.retrieve()`. * Added `request.config.allowlist_options.regexes` to `client.governance.data.policies.update()`. * Added `response.reports[].config.allowlist_options.regexes` to `client.governance.data.policies.list()`. * Added `request.config.allowlist_options.regexes` to `client.governance.data.policies.create()`. * Added `response.report.config.allowlist_options.regexes` to `client.governance.data.policies.create()`. * Added `request.config.allowlist_options.regexes` to `client.governance.data.reports.create()`. * Changed `response` on `client.chat.create()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.14.0) ## api-client-java v0.13.2 API Clients Api-client-java v0.13.2 includes 57 additions, 1 change. **Read more** ## Changes * Added `request.createAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.announcements.create()`. * Added `response.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.announcements.create()`. * Added `request.updateAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.announcements.update()`. * Added `response.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.announcements.update()`. * Added `request.createAnswerRequest.data.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.create()`. * Added `response.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.create()`. * Added `request.editAnswerRequest.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.update()`. * Added `response.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.update()`. * Added `response.answerResult.answer.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.retrieve()`. * Added `response.answerResults[].answer.addedRoles[].group.type.enum(collectionAudience)` to `client.answers.list()`. * Added `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.chat.create()`. * Added `response.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.chat.create()`. * Added `response.chatResult.chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.chat.retrieve()`. * Added `response.chatResults[].chat.createdBy.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.chat.list()`. * Added `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.chat.createstream()`. * Added `response.collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.additems()`. * Added `request.createCollectionRequest.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.create()`. * Added `response.union(class (0)).collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.create()`. * Added `response.collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.deleteitem()`. * Added `request.editCollectionRequest.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.update()`. * Added `response.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.update()`. * Added `response.collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.updateitem()`. * Added `response.collection.addedRoles[].group.type.enum(collectionAudience)` to `client.collections.retrieve()`. * Added `response.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.collections.list()`. * Added `response.documents.Map.union(Document).metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.documents.retrieve()`. * Added `response.documents[].metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.documents.retrievebyfacets()`. * Added `response.gleanAssist.activityInsights[].user.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.insights.retrieve()`. * Added `response.searchResponse.results[].structuredResults[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.messages.retrieve()`. * Added `response.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.pins.update()`. * Added `response.pin.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.pins.retrieve()`. * Added `response.pins[].attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.pins.list()`. * Added `response.attribution.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.pins.create()`. * Added `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.search.queryasadmin()`. * Added `response.results[].structuredResults[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.search.queryasadmin()`. * Added `response.results[].document.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.search.autocomplete()`. * Added `request.feedRequest.categories[].enum(cardStackPromo)` to `client.search.retrievefeed()`. * Added `request.recommendationsRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.search.recommendations()`. * Added `response.results[].structuredResults[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.search.recommendations()`. * Added `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].customEntity.roles[].group.type.enum(collectionAudience)` to `client.search.query()`. * Added `response.results[].structuredResults[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.search.query()`. * Added `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.entities.list()`. * Added `response.results[].relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.entities.readpeople()`. * Added `request.createShortcutRequest.data.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.retrieve()`. * Added `response.shortcuts[].addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.list()`. * Added `request.updateShortcutRequest.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.update()`. * Added `response.shortcut.addedRoles[].group.type.enum(collectionAudience)` to `client.shortcuts.update()`. * Added `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.verification.addreminder()`. * Added `response.documents[].metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.verification.list()`. * Added `response.metadata.lastVerifier.relatedDocuments[].querySuggestion.ranges[].document.metadata.collections[].addedRoles[].group.type.enum(collectionAudience)` to `client.verification.verify()`. * Added `response.report.config.allowlistOptions.regexes` to `client.governance.data.policies.retrieve()`. * Added `request.updateDlpReportRequest.config.allowlistOptions.regexes` to `client.governance.data.policies.update()`. * Added `response.reports[].config.allowlistOptions.regexes` to `client.governance.data.policies.list()`. * Added `request.config.allowlistOptions.regexes` to `client.governance.data.policies.create()`. * Added `response.report.config.allowlistOptions.regexes` to `client.governance.data.policies.create()`. * Added `request.config.allowlistOptions.regexes` to `client.governance.data.reports.create()`. * Changed `response.results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.13.2) ## api-client-go v0.12.1 API Clients Api-client-go v0.12.1 includes 57 additions, 1 change. **Read more** ## Changes * Added `request.CreateAnnouncementRequest.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.announcements.create()`. * Added `response.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.announcements.create()`. * Added `request.UpdateAnnouncementRequest.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.announcements.update()`. * Added `response.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.announcements.update()`. * Added `request.CreateAnswerRequest.Data.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.answers.create()`. * Added `response.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.answers.create()`. * Added `request.EditAnswerRequest.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.answers.update()`. * Added `response.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.answers.update()`. * Added `response.AnswerResult.Answer.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.answers.retrieve()`. * Added `response.AnswerResults[].Answer.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.answers.list()`. * Added `request.ChatRequest.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.chat.create()`. * Added `response.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.chat.create()`. * Added `response.ChatResult.Chat.CreatedBy.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.chat.retrieve()`. * Added `response.ChatResults[].Chat.CreatedBy.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.chat.list()`. * Added `request.ChatRequest.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.chat.createstream()`. * Added `response.Collection.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.additems()`. * Added `request.CreateCollectionRequest.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.create()`. * Added `response.union(class (0)).Collection.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.create()`. * Added `response.Collection.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.deleteitem()`. * Added `request.EditCollectionRequest.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.update()`. * Added `response.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.update()`. * Added `response.Collection.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.updateitem()`. * Added `response.Collection.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.retrieve()`. * Added `response.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.collections.list()`. * Added `response.Documents.Map.union(Document).Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.documents.retrieve()`. * Added `response.Documents[].Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.documents.retrievebyfacets()`. * Added `response.GleanAssist.ActivityInsights[].User.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.insights.retrieve()`. * Added `response.SearchResponse.Results[].StructuredResults[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.messages.retrieve()`. * Added `response.Attribution.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.pins.update()`. * Added `response.Pin.Attribution.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.pins.retrieve()`. * Added `response.Pins[].Attribution.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.pins.list()`. * Added `response.Attribution.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.pins.create()`. * Added `request.SearchRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.search.queryasadmin()`. * Added `response.Results[].StructuredResults[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.search.queryasadmin()`. * Added `response.Results[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.search.autocomplete()`. * Added `request.FeedRequest.Categories[].Enum(cardStackPromo)` to `client.search.retrievefeed()`. * Added `request.RecommendationsRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.search.recommendations()`. * Added `response.Results[].StructuredResults[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.search.recommendations()`. * Added `request.SearchRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].CustomEntity.Roles[].Group.Type.Enum(collectionAudience)` to `client.search.query()`. * Added `response.Results[].StructuredResults[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.search.query()`. * Added `response.Results[].RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.entities.list()`. * Added `response.Results[].RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.entities.readpeople()`. * Added `request.CreateShortcutRequest.Data.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.shortcuts.create()`. * Added `response.Shortcut.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.shortcuts.create()`. * Added `response.Shortcut.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.shortcuts.retrieve()`. * Added `response.Shortcuts[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.shortcuts.list()`. * Added `request.UpdateShortcutRequest.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.shortcuts.update()`. * Added `response.Shortcut.AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.shortcuts.update()`. * Added `response.Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.verification.addreminder()`. * Added `response.Documents[].Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.verification.list()`. * Added `response.Metadata.LastVerifier.RelatedDocuments[].QuerySuggestion.Ranges[].Document.Metadata.Collections[].AddedRoles[].Group.Type.Enum(collectionAudience)` to `client.verification.verify()`. * Added `response.Report.Config.AllowlistOptions.Regexes` to `client.governance.data.policies.retrieve()`. * Added `request.UpdateDlpReportRequest.Config.AllowlistOptions.Regexes` to `client.governance.data.policies.update()`. * Added `response.Reports[].Config.AllowlistOptions.Regexes` to `client.governance.data.policies.list()`. * Added `request.Request.Config.AllowlistOptions.Regexes` to `client.governance.data.policies.create()`. * Added `response.Report.Config.AllowlistOptions.Regexes` to `client.governance.data.policies.create()`. * Added `request.Request.Config.AllowlistOptions.Regexes` to `client.governance.data.reports.create()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.12.1) June 9 2026 ## REST API: changes (endpoints) 2026-06-09 APINoteworthy 1 endpoint added. **Read more** ## Changes * Added endpoint: /agents ## Source * [open-api 04d057a](https://github.com/gleanwork/open-api/commit/04d057a1ee884ee7cd054bc62ae2048c76d90b02) * [open-api 289f5ff](https://github.com/gleanwork/open-api/commit/289f5ffa635cf440780bf159fdd194f4421a4888) * [open-api 4d68227](https://github.com/gleanwork/open-api/commit/4d68227a532a587e419c4f5aa32ec3b0779f071a) * [open-api 89b5b21](https://github.com/gleanwork/open-api/commit/89b5b2118a970f00492d39b262f0e87ecc7a14a1) * [open-api 96940dd](https://github.com/gleanwork/open-api/commit/96940dd45ce8c10d244f30c1230264ffe3717ad9) * [open-api adae527](https://github.com/gleanwork/open-api/commit/adae527852da72eaabde4953bea307550c8be02f) June 7 2026 ## mcp-config v5.2.0 MCP Mcp-config v5.2.0: `mcp-config-schema`: add client type classification (CLIENT\_TYPES, TYPE\_LABELS). **Read more** ## Changes * `mcp-config-schema`: add client type classification (CLIENT\_TYPES, TYPE\_LABELS). ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v5.2.0) * [PR #120](https://github.com/gleanwork/mcp-config/pull/120) June 6 2026 ## mcp-config v5.1.0 MCP Mcp-config v5.1.0: `mcp-config-glean`, `mcp-config-schema`: add Linear as MCP client configuration. **Read more** ## Changes * `mcp-config-glean`, `mcp-config-schema`: add Linear as MCP client configuration. * `mcp-config-schema`: add per-client host icons. * `mcp-config-schema`: add Antigravity CLI MCP client configuration. * Other. * Realign @eslint/js to v9 (restore npm install/publish). * `mcp-config-schema`: generate client constants from configs (single source of truth). ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v5.1.0) * [PR #106](https://github.com/gleanwork/mcp-config/pull/106) * [PR #112](https://github.com/gleanwork/mcp-config/pull/112) * [PR #110](https://github.com/gleanwork/mcp-config/pull/110) * [PR #118](https://github.com/gleanwork/mcp-config/pull/118) * [PR #111](https://github.com/gleanwork/mcp-config/pull/111) June 5 2026 ## api-client-typescript v0.15.0 API Clients Api-client-typescript v0.15.0 includes 3 additions, 54 changes. **Read more** ## Changes * Added `errorCode.enum(corruptItem)` to `client.collections.create()`. * Added `errorCode.enum(corruptItem)` to `client.collections.delete()`. * Added `errorCode.enum(corruptItem)` to `client.collections.update()`. * Changed `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.chat.create()`. * Changed `response` on `client.chat.create()`. * Changed `response` on `client.collections.additems()`. * Changed `request.feedback1.category` on `client.activity.feedback()`. * Changed `request.createAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.announcements.create()`. * Changed `response` on `client.announcements.create()`. * Changed `request.updateAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.announcements.update()`. * Changed `response` on `client.announcements.update()`. * Changed `request.createAnswerRequest.data.addedRoles[].person.relatedDocuments[]` on `client.answers.create()`. * Changed `response` on `client.answers.create()`. * Changed `request.editAnswerRequest.addedRoles[].person.relatedDocuments[]` on `client.answers.update()`. * Changed `response` on `client.answers.update()`. * Changed `response.answerResult.answer` on `client.answers.retrieve()`. * Changed `response.answerResults[].answer` on `client.answers.list()`. * Changed `response.chatResult.chat.createdBy.relatedDocuments[]` on `client.chat.retrieve()`. * Changed `response.chatResults[].chat.createdBy.relatedDocuments[]` on `client.chat.list()`. * Changed `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.chat.createstream()`. * Changed `request.createCollectionRequest.addedRoles[].person.relatedDocuments[]` on `client.collections.create()`. * Changed `response.union(class (0))` on `client.collections.create()`. * Changed `response.collection` on `client.collections.deleteitem()`. * Changed `request.editCollectionRequest.addedRoles[].person.relatedDocuments[]` on `client.collections.update()`. * Changed `response` on `client.collections.update()`. * Changed `response.collection` on `client.collections.updateitem()`. * Changed `response` on `client.collections.retrieve()`. * Changed `response.collections[]` on `client.collections.list()`. * Changed `response.documents.Map.union(Document).metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.documents.retrieve()`. * Changed `response.documents[].metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.documents.retrievebyfacets()`. * Changed `request.insightsRequest` on `client.insights.retrieve()`. * Changed `response` on `client.insights.retrieve()`. * Changed `response.searchResponse.results[].structuredResults[]` on `client.messages.retrieve()`. * Changed `response.attribution.relatedDocuments[]` on `client.pins.update()`. * Changed `response.pin.attribution.relatedDocuments[]` on `client.pins.retrieve()`. * Changed `response.pins[].attribution.relatedDocuments[]` on `client.pins.list()`. * Changed `response.attribution.relatedDocuments[]` on `client.pins.create()`. * Changed `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.search.queryasadmin()`. * Changed `response.results[].structuredResults[]` on `client.search.queryasadmin()`. * Changed `response.results[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.search.autocomplete()`. * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. * Changed `request.recommendationsRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.search.recommendations()`. * Changed `response.results[].structuredResults[]` on `client.search.recommendations()`. * Changed `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.search.query()`. * Changed `response.results[].structuredResults[]` on `client.search.query()`. * Changed `response.results[].relatedDocuments[]` on `client.entities.list()`. * Changed `response.results[].relatedDocuments[]` on `client.entities.readpeople()`. * Changed `request.createShortcutRequest.data.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.create()`. * Changed `response.shortcut.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.create()`. * Changed `response.shortcut.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.retrieve()`. * Changed `response.shortcuts[].addedRoles[].person.relatedDocuments[]` on `client.shortcuts.list()`. * Changed `request.updateShortcutRequest.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.update()`. * Changed `response.shortcut.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.update()`. * Changed `response.metadata.lastVerifier.relatedDocuments[]` on `client.verification.addreminder()`. * Changed `response.documents[].metadata.lastVerifier.relatedDocuments[]` on `client.verification.list()`. * Changed `response.metadata.lastVerifier.relatedDocuments[]` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.15.0) ## api-client-python v0.13.0 API Clients Api-client-python v0.13.0 includes 3 additions, 54 changes. **Read more** ## Changes * Added `error_code.enum(corrupt_item)` to `client.collections.create()`. * Added `error_code.enum(corrupt_item)` to `client.collections.delete()`. * Added `error_code.enum(corrupt_item)` to `client.collections.update()`. * Changed `response` on `client.collections.add_items()`. * Changed `request.feedback1.category` on `client.activity.feedback()`. * Changed `request.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.announcements.create()`. * Changed `response` on `client.announcements.create()`. * Changed `request.body.structured_list[].document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.announcements.update()`. * Changed `response` on `client.announcements.update()`. * Changed `request.data.added_roles[].person.related_documents[]` on `client.answers.create()`. * Changed `response` on `client.answers.create()`. * Changed `request.added_roles[].person.related_documents[]` on `client.answers.update()`. * Changed `response` on `client.answers.update()`. * Changed `response.answer_result.answer` on `client.answers.retrieve()`. * Changed `response.answer_results[].answer` on `client.answers.list()`. * Changed `request.messages[].citations[].source_document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.chat.create()`. * Changed `response.messages[].citations[].source_document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.chat.create()`. * Changed `response.chat_result.chat.created_by.related_documents[]` on `client.chat.retrieve()`. * Changed `response.chat_results[].chat.created_by.related_documents[]` on `client.chat.list()`. * Changed `request.messages[].citations[].source_document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.chat.create_stream()`. * Changed `request.added_roles[].person.related_documents[]` on `client.collections.create()`. * Changed `response.union(class (0))` on `client.collections.create()`. * Changed `response.collection` on `client.collections.delete_item()`. * Changed `request.added_roles[].person.related_documents[]` on `client.collections.update()`. * Changed `response` on `client.collections.update()`. * Changed `response.collection` on `client.collections.update_item()`. * Changed `response` on `client.collections.retrieve()`. * Changed `response.collections[]` on `client.collections.list()`. * Changed `response.documents.Map.union(Document).metadata.author.related_documents[].results[].structured_results[].answer` on `client.documents.retrieve()`. * Changed `response.documents[].metadata.author.related_documents[].results[].structured_results[].answer` on `client.documents.retrieve_by_facets()`. * Changed `request` on `client.insights.retrieve()`. * Changed `response` on `client.insights.retrieve()`. * Changed `response.search_response.results[].structured_results[]` on `client.messages.retrieve()`. * Changed `response.attribution.related_documents[]` on `client.pins.update()`. * Changed `response.pin.attribution.related_documents[]` on `client.pins.retrieve()`. * Changed `response.pins[].attribution.related_documents[]` on `client.pins.list()`. * Changed `response.attribution.related_documents[]` on `client.pins.create()`. * Changed `request.source_document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.search.query_as_admin()`. * Changed `response.results[].structured_results[]` on `client.search.query_as_admin()`. * Changed `response.results[].document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.search.autocomplete()`. * Changed `request.categories[]` on `client.search.retrieve_feed()`. * Changed `response.results[]` on `client.search.retrieve_feed()`. * Changed `request.source_document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.search.recommendations()`. * Changed `response.results[].structured_results[]` on `client.search.recommendations()`. * Changed `request.source_document.metadata.author.related_documents[].results[].structured_results[].answer` on `client.search.query()`. * Changed `response.results[].structured_results[]` on `client.search.query()`. * Changed `response.results[].related_documents[]` on `client.entities.list()`. * Changed `response.results[].related_documents[]` on `client.entities.read_people()`. * Changed `request.data.added_roles[].person.related_documents[]` on `client.shortcuts.create()`. * Changed `response.shortcut.added_roles[].person.related_documents[]` on `client.shortcuts.create()`. * Changed `response.shortcut.added_roles[].person.related_documents[]` on `client.shortcuts.retrieve()`. * Changed `response.shortcuts[].added_roles[].person.related_documents[]` on `client.shortcuts.list()`. * Changed `request.added_roles[].person.related_documents[]` on `client.shortcuts.update()`. * Changed `response.shortcut.added_roles[].person.related_documents[]` on `client.shortcuts.update()`. * Changed `response.metadata.last_verifier.related_documents[]` on `client.verification.add_reminder()`. * Changed `response.documents[].metadata.last_verifier.related_documents[]` on `client.verification.list()`. * Changed `response.metadata.last_verifier.related_documents[]` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.13.0) ## api-client-java v0.13.1 API Clients Api-client-java v0.13.1 includes 1 addition. **Read more** ## Changes * Added `response.status[202]` to `client.chat.create()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.13.1) ## api-client-java v0.13.0 API Clients Api-client-java v0.13.0 includes 3 additions, 54 changes. **Read more** ## Changes * Added `errorCode.enum(corruptItem)` to `client.collections.create()`. * Added `errorCode.enum(corruptItem)` to `client.collections.delete()`. * Added `errorCode.enum(corruptItem)` to `client.collections.update()`. * Changed `response` on `client.collections.additems()`. * Changed `request.feedback1.category` on `client.activity.feedback()`. * Changed `request.createAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.announcements.create()`. * Changed `response` on `client.announcements.create()`. * Changed `request.updateAnnouncementRequest.body.structuredList[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.announcements.update()`. * Changed `response` on `client.announcements.update()`. * Changed `request.createAnswerRequest.data.addedRoles[].person.relatedDocuments[]` on `client.answers.create()`. * Changed `response` on `client.answers.create()`. * Changed `request.editAnswerRequest.addedRoles[].person.relatedDocuments[]` on `client.answers.update()`. * Changed `response` on `client.answers.update()`. * Changed `response.answerResult.answer` on `client.answers.retrieve()`. * Changed `response.answerResults[].answer` on `client.answers.list()`. * Changed `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.chat.create()`. * Changed `response.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.chat.create()`. * Changed `response.chatResult.chat.createdBy.relatedDocuments[]` on `client.chat.retrieve()`. * Changed `response.chatResults[].chat.createdBy.relatedDocuments[]` on `client.chat.list()`. * Changed `request.chatRequest.messages[].citations[].sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.chat.createstream()`. * Changed `request.createCollectionRequest.addedRoles[].person.relatedDocuments[]` on `client.collections.create()`. * Changed `response.union(class (0))` on `client.collections.create()`. * Changed `response.collection` on `client.collections.deleteitem()`. * Changed `request.editCollectionRequest.addedRoles[].person.relatedDocuments[]` on `client.collections.update()`. * Changed `response` on `client.collections.update()`. * Changed `response.collection` on `client.collections.updateitem()`. * Changed `response` on `client.collections.retrieve()`. * Changed `response.collections[]` on `client.collections.list()`. * Changed `response.documents.Map.union(Document).metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.documents.retrieve()`. * Changed `response.documents[].metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.documents.retrievebyfacets()`. * Changed `request.insightsRequest` on `client.insights.retrieve()`. * Changed `response` on `client.insights.retrieve()`. * Changed `response.searchResponse.results[].structuredResults[]` on `client.messages.retrieve()`. * Changed `response.attribution.relatedDocuments[]` on `client.pins.update()`. * Changed `response.pin.attribution.relatedDocuments[]` on `client.pins.retrieve()`. * Changed `response.pins[].attribution.relatedDocuments[]` on `client.pins.list()`. * Changed `response.attribution.relatedDocuments[]` on `client.pins.create()`. * Changed `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.search.queryasadmin()`. * Changed `response.results[].structuredResults[]` on `client.search.queryasadmin()`. * Changed `response.results[].document.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.search.autocomplete()`. * Changed `request.feedRequest.categories[]` on `client.search.retrievefeed()`. * Changed `response.results[]` on `client.search.retrievefeed()`. * Changed `request.recommendationsRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.search.recommendations()`. * Changed `response.results[].structuredResults[]` on `client.search.recommendations()`. * Changed `request.searchRequest.sourceDocument.metadata.author.relatedDocuments[].results[].structuredResults[].answer` on `client.search.query()`. * Changed `response.results[].structuredResults[]` on `client.search.query()`. * Changed `response.results[].relatedDocuments[]` on `client.entities.list()`. * Changed `response.results[].relatedDocuments[]` on `client.entities.readpeople()`. * Changed `request.createShortcutRequest.data.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.create()`. * Changed `response.shortcut.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.create()`. * Changed `response.shortcut.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.retrieve()`. * Changed `response.shortcuts[].addedRoles[].person.relatedDocuments[]` on `client.shortcuts.list()`. * Changed `request.updateShortcutRequest.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.update()`. * Changed `response.shortcut.addedRoles[].person.relatedDocuments[]` on `client.shortcuts.update()`. * Changed `response.metadata.lastVerifier.relatedDocuments[]` on `client.verification.addreminder()`. * Changed `response.documents[].metadata.lastVerifier.relatedDocuments[]` on `client.verification.list()`. * Changed `response.metadata.lastVerifier.relatedDocuments[]` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.13.0) ## api-client-go v0.12.0 API Clients Api-client-go v0.12.0 includes 3 additions, 54 changes. **Read more** ## Changes * Added `error.ErrorCode.Enum(corruptItem)` to `client.collections.create()`. * Added `error.ErrorCode.Enum(corruptItem)` to `client.collections.delete()`. * Added `error.ErrorCode.Enum(corruptItem)` to `client.collections.update()`. * Changed `request.ChatRequest.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.chat.create()`. * Changed `response` on `client.chat.create()`. * Changed `response` on `client.collections.additems()`. * Changed `request.Feedback1.Category` on `client.activity.feedback()`. * Changed `request.CreateAnnouncementRequest.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.announcements.create()`. * Changed `response` on `client.announcements.create()`. * Changed `request.UpdateAnnouncementRequest.Body.StructuredList[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.announcements.update()`. * Changed `response` on `client.announcements.update()`. * Changed `request.CreateAnswerRequest.Data.AddedRoles[].Person.RelatedDocuments[]` on `client.answers.create()`. * Changed `response` on `client.answers.create()`. * Changed `request.EditAnswerRequest.AddedRoles[].Person.RelatedDocuments[]` on `client.answers.update()`. * Changed `response` on `client.answers.update()`. * Changed `response.AnswerResult.Answer` on `client.answers.retrieve()`. * Changed `response.AnswerResults[].Answer` on `client.answers.list()`. * Changed `response.ChatResult.Chat.CreatedBy.RelatedDocuments[]` on `client.chat.retrieve()`. * Changed `response.ChatResults[].Chat.CreatedBy.RelatedDocuments[]` on `client.chat.list()`. * Changed `request.ChatRequest.Messages[].Citations[].SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.chat.createstream()`. * Changed `request.CreateCollectionRequest.AddedRoles[].Person.RelatedDocuments[]` on `client.collections.create()`. * Changed `response.union(class (0))` on `client.collections.create()`. * Changed `response.Collection` on `client.collections.deleteitem()`. * Changed `request.EditCollectionRequest.AddedRoles[].Person.RelatedDocuments[]` on `client.collections.update()`. * Changed `response` on `client.collections.update()`. * Changed `response.Collection` on `client.collections.updateitem()`. * Changed `response` on `client.collections.retrieve()`. * Changed `response.Collections[]` on `client.collections.list()`. * Changed `response.Documents.Map.union(Document).Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.documents.retrieve()`. * Changed `response.Documents[].Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.documents.retrievebyfacets()`. * Changed `request.InsightsRequest` on `client.insights.retrieve()`. * Changed `response` on `client.insights.retrieve()`. * Changed `response.SearchResponse.Results[].StructuredResults[]` on `client.messages.retrieve()`. * Changed `response.Attribution.RelatedDocuments[]` on `client.pins.update()`. * Changed `response.Pin.Attribution.RelatedDocuments[]` on `client.pins.retrieve()`. * Changed `response.Pins[].Attribution.RelatedDocuments[]` on `client.pins.list()`. * Changed `response.Attribution.RelatedDocuments[]` on `client.pins.create()`. * Changed `request.SearchRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.search.queryasadmin()`. * Changed `response.Results[].StructuredResults[]` on `client.search.queryasadmin()`. * Changed `response.Results[].Document.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.search.autocomplete()`. * Changed `request.FeedRequest.Categories[]` on `client.search.retrievefeed()`. * Changed `response.Results[]` on `client.search.retrievefeed()`. * Changed `request.RecommendationsRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.search.recommendations()`. * Changed `response.Results[].StructuredResults[]` on `client.search.recommendations()`. * Changed `request.SearchRequest.SourceDocument.Metadata.Author.RelatedDocuments[].Results[].StructuredResults[].Answer` on `client.search.query()`. * Changed `response.Results[].StructuredResults[]` on `client.search.query()`. * Changed `response.Results[].RelatedDocuments[]` on `client.entities.list()`. * Changed `response.Results[].RelatedDocuments[]` on `client.entities.readpeople()`. * Changed `request.CreateShortcutRequest.Data.AddedRoles[].Person.RelatedDocuments[]` on `client.shortcuts.create()`. * Changed `response.Shortcut.AddedRoles[].Person.RelatedDocuments[]` on `client.shortcuts.create()`. * Changed `response.Shortcut.AddedRoles[].Person.RelatedDocuments[]` on `client.shortcuts.retrieve()`. * Changed `response.Shortcuts[].AddedRoles[].Person.RelatedDocuments[]` on `client.shortcuts.list()`. * Changed `request.UpdateShortcutRequest.AddedRoles[].Person.RelatedDocuments[]` on `client.shortcuts.update()`. * Changed `response.Shortcut.AddedRoles[].Person.RelatedDocuments[]` on `client.shortcuts.update()`. * Changed `response.Metadata.LastVerifier.RelatedDocuments[]` on `client.verification.addreminder()`. * Changed `response.Documents[].Metadata.LastVerifier.RelatedDocuments[]` on `client.verification.list()`. * Changed `response.Metadata.LastVerifier.RelatedDocuments[]` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.12.0) May 18 2026 ## Custom Metadata API is Generally Available APINoteworthy The Custom Metadata API is now generally available — a new capability for attaching structured metadata to documents from any datasource, including native connectors and custom datasources, without re-indexing the source document. **Read more** ## What's new * **New endpoints** under `/rest/api/index`: * `PUT /custom-metadata/schema/{groupName}` — create or update a metadata group schema * `GET /custom-metadata/schema/{groupName}` — retrieve a metadata group schema * `DELETE /custom-metadata/schema/{groupName}` — remove a metadata group schema * `PUT /document/{docId}/custom-metadata/{groupName}` — add or update metadata on a document * `DELETE /document/{docId}/custom-metadata/{groupName}` — remove metadata from a document * **Property types**: `TEXT`, `PICKLIST`, `TEXTLIST`, `MULTIPICKLIST`, with optional `skipIndexing` to keep values retrievable but excluded from full-text search. * **Scoped tokens** — manage permissions per metadata group via `custommetadata:`, or globally with `custommetadata:global_scope`. * **Querying** — values are searchable as facets (`:`) and retrievable through the Client API `getDocuments` endpoint with `includeFields: ["CUSTOM_METADATA"]`. ## When to use it Custom Metadata works with any document already indexed in Glean. Use it to enrich documents from native connectors (Google Drive, Confluence, Jira, etc.) or custom datasources without re-uploading them. For a side-by-side comparison with Custom Properties (which require re-indexing the document), see [Custom Properties vs Custom Metadata](/api-info/indexing/custom-metadata/custom-properties-vs-custom-metadata.md). ## Documentation * [Custom Metadata Overview](/api-info/indexing/custom-metadata/overview.md) * [Authentication](/api-info/indexing/custom-metadata/authentication.md) * [Schema Management](/api-info/indexing/custom-metadata/schema-management.md) * [Indexing Metadata](/api-info/indexing/custom-metadata/indexing-metadata.md) * [Querying](/api-info/indexing/custom-metadata/querying.md) May 13 2026 ## REST API: changes (endpoints) 2026-05-13 APINoteworthy 2 endpoints added. **Read more** ## Changes * Added endpoint: /custom-metadata/schema/{groupName} * Added endpoint: /document/{docId}/custom-metadata/{groupName} ## Source * [open-api 1d56839](https://github.com/gleanwork/open-api/commit/1d568391d821bd180625c82512f0000265073362) * [open-api 6249d56](https://github.com/gleanwork/open-api/commit/6249d56b0e04656fd917ec7477aa135a78e84305) * [open-api 647eaa3](https://github.com/gleanwork/open-api/commit/647eaa38c051fec5593ab69cfdbc89e96fe5932f) * [open-api 7a2b665](https://github.com/gleanwork/open-api/commit/7a2b665f271485fea9181f008202a3f98536fcae) May 12 2026 ## REST API: changes (endpoints) 2026-05-12 APIBreakingAction requiredNoteworthy Breaking change: 1 endpoint added, 1 endpoint removed. **Read more** ## Action Required * Update callers to stop using /actions/{actionPackId}/auth. ## Changes * Added endpoint: /actions/actionpack/{actionPackId}/auth ## Breaking Changes * Removed endpoint: /actions/{actionPackId}/auth (breaking change) ## Source * [open-api 02f647c](https://github.com/gleanwork/open-api/commit/02f647c1ae9b636e06115733a1d5e179a31578d2) * [open-api 222faa4](https://github.com/gleanwork/open-api/commit/222faa48c244289ca936bbc8c880543c048b73e0) * [open-api 3c5f04e](https://github.com/gleanwork/open-api/commit/3c5f04e4172b07ada26ee79f7f6751f1720fb04c) * [open-api 439d1d2](https://github.com/gleanwork/open-api/commit/439d1d2144619900359c140129d902e6b28e8688) * [open-api 51db945](https://github.com/gleanwork/open-api/commit/51db945590372dc3832099eff8ee01d3ce66c162) * [open-api 5a6fddc](https://github.com/gleanwork/open-api/commit/5a6fddcf74166faa81f53f824a4d389e61800e83) * [open-api 5f2faf1](https://github.com/gleanwork/open-api/commit/5f2faf1fd784e237def639db02464cccdd827c1d) * [open-api 81d283b](https://github.com/gleanwork/open-api/commit/81d283bdc17d07db5fb7c2de0801c40e8bd017c0) May 11 2026 ## REST API: changes (endpoints) 2026-05-11 APINoteworthy 2 endpoints added. **Read more** ## Changes * Added endpoint: /api/index/v1/debug/{datasource}/document/events * Added endpoint: /debug/{datasource}/document/events ## Source * [open-api 31144d0](https://github.com/gleanwork/open-api/commit/31144d0a1a6d47a7a15e0df5fe47d4d9e1f2a846) * [open-api 565ee21](https://github.com/gleanwork/open-api/commit/565ee21edfefe7b9ebedd2f3c568054dbc340901) * [open-api 8d3a911](https://github.com/gleanwork/open-api/commit/8d3a9118a3de11737fa4eda5bcdb850867067613) * [open-api 968befb](https://github.com/gleanwork/open-api/commit/968befb8c149a90c5954f43a49a3c40ae5834772) * [open-api ae19c79](https://github.com/gleanwork/open-api/commit/ae19c79f11106c658bf1061dcc8d4ca5c9a26f07) * [open-api eb3e8be](https://github.com/gleanwork/open-api/commit/eb3e8be368aaa09d127d325de8413064635e76dd) May 7 2026 ## REST API: changes (endpoints) 2026-05-07 APINoteworthy 2 endpoints added. **Read more** ## Changes * Added endpoint: /actions/{actionPackId}/auth * Added endpoint: /chat-files/{fileId} ## Source * [open-api 368a20e](https://github.com/gleanwork/open-api/commit/368a20e9e9b003c93c2a9bc7a34d0672cd764a20) * [open-api 36ccb9c](https://github.com/gleanwork/open-api/commit/36ccb9ca0c45287b1f1f6d876940315f09e51ab0) * [open-api 49f327d](https://github.com/gleanwork/open-api/commit/49f327d09ca52c6dcd7b5ef92c1ba891e753d9bb) * [open-api 50b0df5](https://github.com/gleanwork/open-api/commit/50b0df50b50693714bddc3e93c6cfa281962767d) * [open-api 6a7c06c](https://github.com/gleanwork/open-api/commit/6a7c06cb55082b8df19cf4111a4d1fefd6efb22e) * [open-api 6bd6a08](https://github.com/gleanwork/open-api/commit/6bd6a0894942908a5a808538273f228c59e2f41f) * [open-api 6c3c728](https://github.com/gleanwork/open-api/commit/6c3c728ff527a3e06c2616e820057fb69dd6406f) * [open-api 8d724f8](https://github.com/gleanwork/open-api/commit/8d724f8d913aa80050700dcc64f42f202943ada9) May 5 2026 ## REST API: changes (endpoints) 2026-05-05 APINoteworthy 1 endpoint added. **Read more** ## Changes * Added endpoint: /people/{person\_id}/photo ## Source * [open-api 2800e7a](https://github.com/gleanwork/open-api/commit/2800e7a577fa6d58a60b0659a83e2b4cd07ee575) * [open-api 2d3443c](https://github.com/gleanwork/open-api/commit/2d3443c7126e7b9b8504012ebc1b576f478f719e) * [open-api 61c1f35](https://github.com/gleanwork/open-api/commit/61c1f35f0e4d5c3cad1fe94c398a131a42628273) * [open-api 73555b8](https://github.com/gleanwork/open-api/commit/73555b80908616f7b414a05add5defcf86178a0c) * [open-api 8b52a73](https://github.com/gleanwork/open-api/commit/8b52a73a0470863ec091b92ba321dbfd4bdb3d5d) * [open-api 8f8bfbd](https://github.com/gleanwork/open-api/commit/8f8bfbd45f91882595938f16493847a4488a5ac9) * [open-api a6d97ad](https://github.com/gleanwork/open-api/commit/a6d97ad3f1a75fee061fbad5d48f402834e3531e) * [open-api d485d2c](https://github.com/gleanwork/open-api/commit/d485d2c00fb0fcbba724b8fefe66283b2d55e155) May 4 2026 ## configure-mcp-server v3.2.0 MCP Configure-mcp-server v3.2.0: Friendly error when no local MCP server is configured. **Read more** ## Changes * Friendly error when no local MCP server is configured. ## Source * [Release notes](https://github.com/gleanwork/configure-mcp-server/releases/tag/v3.2.0) * [PR #78](https://github.com/gleanwork/configure-mcp-server/pull/78) May 2 2026 ## mcp-config v5.0.0 MCPBreakingAction requiredDeprecated Breaking change: `mcp-config-glean`, `mcp-config-schema`: drop local MCP path; add cliVersion pin support. **Read more** ## Action Required * Review this breaking change before upgrading: `mcp-config-glean`, `mcp-config-schema`: drop local MCP path; add cliVersion pin support. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * `mcp-config-glean`, `mcp-config-schema`: drop local MCP path; add cliVersion pin support. ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v5.0.0) * [PR #108](https://github.com/gleanwork/mcp-config/pull/108) April 23 2026 ## glean-indexing-sdk v1.0.0b2 Glean Indexing SDK Glean Indexing SDK v1.0.0b2 adds connector options for bulk indexing and fixes datasource configuration handling. **Read more** ## Changes * Added ConnectorOptions and updated documentation. * Fixed configure\_datasource() to use attribute access and avoid camelCase keyword arguments. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v1.0.0b2) * [PR #15](https://github.com/gleanwork/glean-indexing-sdk/pull/15) * [PR #17](https://github.com/gleanwork/glean-indexing-sdk/pull/17) April 15 2026 ## api-client-typescript v0.14.18 API Clients Added `feedbackUserCount` to `client.insights.retrieve()` and `issueFilter` to `governance.createfindingsexport()`. **Read more** ## Changes * Added `feedbackUserCount` to `client.insights.retrieve()` and `issueFilter` to `governance.createfindingsexport()`. Changed `issues` on `client.activity.feedback()`, `response` on `governance.createfindingsexport()`, and `exports` on `governance.listfindingsexports()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.18) ## api-client-python v0.12.22 API Clients Added `feedback_user_count` to `client.insights.retrieve()` and `issue_filter` to `governance.createfindingsexport()`. Changed `response` on `governance.createfindingsexport()` and `exports` on `governance.listfindingsexports()`. **Read more** ## Changes * Added `feedback_user_count` to `client.insights.retrieve()` and `issue_filter` to `governance.createfindingsexport()`. Changed `response` on `governance.createfindingsexport()` and `exports` on `governance.listfindingsexports()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.22) ## api-client-java v0.12.37 API Clients Added `feedbackUserCount` to `client.insights.retrieve()` and `issueFilter` to `governance.createfindingsexport()`. Changed `response` on `governance.createfindingsexport()` and `exports` on `governance.listfindingsexports()`. **Read more** ## Changes * Added `feedbackUserCount` to `client.insights.retrieve()` and `issueFilter` to `governance.createfindingsexport()`. Changed `response` on `governance.createfindingsexport()` and `exports` on `governance.listfindingsexports()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.37) ## api-client-go v0.11.42 API Clients Added `FeedbackUserCount` to `client.insights.retrieve()` and `IssueFilter` to `governance.createfindingsexport()`. **Read more** ## Changes * Added `FeedbackUserCount` to `client.insights.retrieve()` and `IssueFilter` to `governance.createfindingsexport()`. Changed `Issues` on `client.activity.feedback()`, `response` on `governance.createfindingsexport()`, and `Exports` on `governance.listfindingsexports()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.42) April 13 2026 ## api-client-typescript v0.14.17 API Clients Added lastDraftSavedAt workflow field to search feed results in glean.client.search.retrieveFeed() and statuses filter field to governance findings export endpoints glean.governance.createfindingsexport(). **Read more** ## Changes * Added lastDraftSavedAt workflow field to search feed results in glean.client.search.retrieveFeed() and statuses filter field to governance findings export endpoints glean.governance.createfindingsexport() and glean.governance.listfindingsexports(). ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.17) ## api-client-go v0.11.41 API Clients Added LastDraftSavedAt workflow field to search feed results in Glean.Client.Search.RetrieveFeed() and Statuses filter field to governance findings export endpoints Glean.Governance.Createfindingsexport(). **Read more** ## Changes * Added LastDraftSavedAt workflow field to search feed results in Glean.Client.Search.RetrieveFeed() and Statuses filter field to governance findings export endpoints Glean.Governance.Createfindingsexport() and Glean.Governance.Listfindingsexports(). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.41) April 9 2026 ## api-client-go v0.11.40 API Clients Changed the AgentsResponse field in Glean.Client.Insights.Retrieve() and added the spreadsheetType UgcType enum value to search feed results in Glean.Client.Search.RetrieveFeed(). **Read more** ## Changes * Changed the AgentsResponse field in Glean.Client.Insights.Retrieve() and added the spreadsheetType UgcType enum value to search feed results in Glean.Client.Search.RetrieveFeed(). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.40) April 8 2026 ## glean-agent-toolkit 0.5.0 Glean Agent Toolkit Glean Agent Toolkit improved developer setup and compatibility with installable skills, injectable GleanContext, unified adapter extras, retry and environment configuration updates, and LangGraph 1.x support. **Read more** ## Changes * Glean Agent Toolkit improved developer setup and compatibility with installable skills, injectable GleanContext, unified adapter extras, retry and environment configuration updates, and LangGraph 1.x support. ## Source * [Release notes](https://github.com/gleanwork/glean-agent-toolkit/releases/tag/0.5.0) March 27 2026 ## api-client-typescript v0.14.14 API Clients Added partiallyProcessed status enum to chat file metadata in glean.client.chat.create(), glean.client.chat.retrieve(), glean.client.chat.createStream(), glean.client.chat.uploadFiles(), and glean.client.chat.retrieveFiles(). **Read more** ## Changes * Added nativeAppUrl field to document indexing requests in glean.indexing.documents.addOrUpdate(), glean.indexing.documents.index(), and glean.indexing.documents.bulkIndex(). ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.14) ## api-client-python v0.12.18 API ClientsNoteworthy The API client now supports a file metadata status in chat request and response citation objects and adds to document indexing requests. **Read more** ## Changes * Added in request citations and response message citations, and in request citations. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.18) ## api-client-java v0.12.33 API ClientsNoteworthy Added support for the metadata status in chat citation and file fields, and added to document indexing requests. **Read more** ## Changes * Added to chat create and createStream request citation source files. * Added to chat create, retrieve, uploadFiles, and retrieveFiles response citation and file metadata. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.33) ## api-client-go v0.11.38 API Clients The Go API client adds the status enum to chat citation and file metadata fields and adds to document indexing requests. **Read more** ## Changes * Added to chat create and createStream request citation source file metadata. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.38) March 25 2026 ## api-client-typescript v0.14.13 API Clients Released TypeScript API client v0.14.13 for the Glean REST API, published on npm as @gleanwork/api-client. **Read more** ## Changes * Generated from OpenAPI Doc 0.9.0 using Speakeasy CLI 1.759.2. * Includes full TypeScript types, async/await support, and fetch-based HTTP for browser and Node.js. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.13) ## api-client-python v0.12.17 API Clients Released Python API client v0.12.17 generated from OpenAPI Doc 0.9.0 using Speakeasy CLI 1.759.2 (2.869.23). **Read more** ## Changes * Generated Python SDK version v0.12.17 - Published Python package release v0.12.17 on PyPI. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.17) ## api-client-java v0.12.32 API Clients Released Java API client v0.12.32 based on OpenAPI Doc 0.9.0 using Speakeasy CLI 1.759.2 (2.869.23). **Read more** ## Changes * Generated Java SDK version v0.12.32 - Published Java SDK v0.12.32 to Maven Central. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.32) ## api-client-go v0.11.37 API Clients Released Go API client v0.11.37 generated from OpenAPI Doc 0.9.0 using Speakeasy CLI 1.759.2 (2.869.23). **Read more** ## Changes * Updated Go API client to version v0.11.37 - Regenerated client from OpenAPI Doc 0.9.0 - Generation performed with Speakeasy CLI 1.759.2 (2.869.23). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.37) March 23 2026 ## api-client-typescript v0.14.12 API Clients The request.feedRequest.categories enum value weeklyMeetings was added and response.results was changed in glean.client.search.retrieveFeed(). **Read more** ## Changes * Added weeklyMeetings category enum. * Changed feed response results. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.12) ## api-client-typescript v0.14.11 API Clients Added sourceCustomEntity to message citations in glean.client.chat.create(), glean.client.chat.retrieve(), and glean.client.chat.createStream(). **Read more** ## Changes * Added issues enum to glean.governance.createfindingsexport() export type. * Changed response.overviewResponse in glean.client.insights.retrieve(). ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.11) ## api-client-python v0.12.16 API Clients The request.categories enum value weekly\_meetings was added and response.results was changed in glean.client.search.retrieve\_feed(). **Read more** ## Changes * Added weekly\_meetings category enum. * Changed feed response results. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.16) ## api-client-python v0.12.15 API Clients Glean-api-client 0.12.15 adds citation fields to chat create, retrieve, and create\_stream flows, changes in insights retrieve, and adds as an export type for findings export. **Read more** ## Changes * And add , and also adds - adds - changes , and adds. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.15) ## api-client-java v0.12.31 API Clients The request.feedRequest.categories enum value weeklyMeetings was added and response.results was changed in glean.client.search.retrieveFeed(). **Read more** ## Changes * Added weeklyMeetings category enum. * Changed feed response results. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.31) ## api-client-java v0.12.30 API Clients Chat citations now include across chat create, retrieve, and streaming APIs, changed , and findings export added an export type. **Read more** ## Changes * Added. * Changed and added. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.30) ## api-client-go v0.11.36 API Clients The request.FeedRequest.Categories enum value weeklyMeetings was added and response.Results was changed in Glean.Client.Search.RetrieveFeed(). **Read more** ## Changes * Added weeklyMeetings category enum. * Changed feed response results. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.36) ## api-client-go v0.11.35 API Clients Added SourceCustomEntity to message citations in Glean.Client.Chat.Create(), Glean.Client.Chat.Retrieve(), and Glean.Client.Chat.CreateStream(). **Read more** ## Changes * Added issues enum to Glean.Governance.Createfindingsexport() export type. * Changed response.OverviewResponse in Glean.Client.Insights.Retrieve(). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.35) March 19 2026 ## api-client-typescript v0.14.10 API Clients Released Glean TypeScript API client v0.14.10 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Updated generated TypeScript SDK to version 0.14.10. * Published @gleanwork/api-client NPM package at version 0.14.10. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.10) ## api-client-python v0.12.14 API Clients Released Python API client v0.12.14 based on OpenAPI Doc 0.9.0 and updated Speakeasy CLI tooling. **Read more** ## Changes * Updated client generation to use OpenAPI Doc 0.9.0. * Regenerated Python SDK at version 0.12.14. * Published Python client v0.12.14 to PyPI. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.14) ## api-client-java v0.12.29 API Clients Released Java API client version 0.12.29 based on OpenAPI Doc 0.9.0 and updated Speakeasy CLI tooling. **Read more** ## Changes * Generated Java SDK v0.12.29 - Published Java SDK v0.12.29 to Maven Central. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.29) ## api-client-go v0.11.34 API Clients Released Go API client v0.11.34 generated from OpenAPI Doc 0.9.0. **Read more** ## Changes * Based on OpenAPI Doc version 0.9.0 - Regenerated Go SDK (v0.11.34) using updated inputs - Published Go API client release v0.11.34. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.34) March 18 2026 ## api-client-typescript v0.14.9 API Clients Updated the Glean TypeScript API client to v0.14.9 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * TypeScript SDK regenerated as version 0.14.9. * Published NPM package @gleanwork/api-client at version 0.14.9. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.9) ## api-client-python v0.12.13 API Clients Released Python API client v0.12.13 based on OpenAPI Doc 0.9.0 and updated Speakeasy CLI tooling. **Read more** ## Changes * Generated python v0.12.13 from OpenAPI Doc 0.9.0 - Published python client as PyPI release v0.12.13. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.13) ## api-client-java v0.12.28 API Clients Updated Java API client to version 0.12.28 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.757.0 (2.866.0), with a new release published to Maven Central. **Read more** ## Changes * Generated Java client: v0.12.28 - OpenAPI specification: 0.9.0; Speakeasy CLI: 1.757.0 (2.866.0) - Maven Central release: v0.12.28. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.28) ## api-client-go v0.11.33 API Clients Released Go API client v0.11.33 based on OpenAPI Doc 0.9.0 using Speakeasy CLI 1.757.0 (2.866.0). **Read more** ## Changes * Updated Go client generation to OpenAPI Doc 0.9.0. * Regenerated Go SDK as version v0.11.33. * Published Go API client release v0.11.33. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.33) March 17 2026 ## api-client-typescript v0.14.8 API ClientsBreaking This release updates multiple request/response schemas (including chat, search, collections, answers, pins, announcements, entities, shortcuts, verification) and adds new datasource configuration endpoints, with several breaking changes. **Read more** ## Changes * This release updates multiple request/response schemas (including chat, search, collections, answers, pins, announcements, entities, shortcuts, verification) and adds new datasource configuration endpoints, with several breaking changes. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.8) ## api-client-python v0.12.12 API Clients Released Python API client v0.12.12 generated from OpenAPI Doc 0.9.0 using Speakeasy CLI 1.755.1 (2.865.2). **Read more** ## Changes * Generated Python SDK version v0.12.12 - Published PyPI release v0.12.12. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.12) ## api-client-java v0.12.27 API Clients Updated Java API client to version 0.12.27 based on OpenAPI Doc 0.9.0 and the latest Speakeasy CLI tooling. **Read more** ## Changes * Regenerated Java SDK at v0.12.27 - Published Java SDK v0.12.27 to Maven Central. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.27) ## api-client-go v0.11.32 API Clients Released Go API client version 0.11.32 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Updated Go API client to v0.11.32 - Regenerated from OpenAPI Doc 0.9.0 using Speakeasy CLI 1.755.1 (2.865.2). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.32) March 16 2026 ## api-client-python v0.12.11 API Clients The response.chat field was added to glean.client.chat.create() and request.categories and response.results were changed in glean.client.search.retrieve\_feed(). **Read more** ## Changes * Added chat field to chat create response. * Changed feed request categories and response results. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.11) ## api-client-java v0.12.26 API Clients The response.chat field was added to glean.client.chat.create() and request.feedRequest.categories and response.results were changed in glean.client.search.retrieveFeed(). **Read more** ## Changes * Added chat field to chat create response. * Changed feed request categories and response results. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.26) ## api-client-go v0.11.31 API Clients This release adds a new chat response object and updates feed search request and result structures in the Go API client. **Read more** ## Changes * : added - : changed - : changed. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.31) March 11 2026 ## api-client-python v0.12.10 API Clients Added new auth metadata fields to chat APIs and a name field to agent schema retrieval responses. **Read more** ## Changes * Added request and response. * Added response. * Added request to and response. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.10) ## api-client-java v0.12.25 API Clients Added tokenEndpointAuthMethod to action metadata auth fields in glean.client.chat.create(), glean.client.chat.retrieve(), and glean.client.chat.createStream(). **Read more** ## Changes * Added name field to the glean.client.agents.retrieveSchemas() response. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.25) ## api-client-go v0.11.30 API Clients Added TokenEndpointAuthMethod to action metadata auth fields in Glean.Client.Chat.Create(), Glean.Client.Chat.Retrieve(), and Glean.Client.Chat.CreateStream(). **Read more** ## Changes * Added Name field to the Glean.Client.Agents.RetrieveSchemas() response. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.30) March 9 2026 ## api-client-python v0.12.9 API ClientsAction requiredDeprecated Added glean.datasources.get\_datasource\_instance\_configuration() and glean.datasources.update\_datasource\_instance\_configuration(). **Read more** ## Action Required * Plan migration away from glean.indexing.people.bulk\_index(). ## Changes * Added 404 error type to glean.client.agents.retrieve(). * Changed snippet structures across search, chat. * Added glean.datasources.get\_datasource\_instance\_configuration() and glean.datasources.update\_datasource\_instance\_configuration(). * Changed snippet structures across search, chat, collections, and entity endpoints. ## Deprecated * Deprecated glean.indexing.people.bulk\_index(). ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.9) ## api-client-java v0.12.24 API ClientsAction requiredDeprecated This release updates multiple client request/response schemas (including snippets and chat messages), introduces new datasource configuration endpoints, and deprecates a people indexing API. **Read more** ## Action Required * Plan migration away from deprecated behavior. ## Changes * This release updates multiple client request/response schemas (including snippets and chat messages), introduces new datasource configuration endpoints, and deprecates a people indexing API. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.24) ## api-client-go v0.11.29 API ClientsBreaking This release introduces widespread response and snippet schema changes across the Go client, multiple breaking changes to chat and search APIs, new datasource configuration endpoints, and an updated agents error model. **Read more** ## Changes * This release introduces widespread response and snippet schema changes across the Go client, multiple breaking changes to chat and search APIs, new datasource configuration endpoints, and an updated agents error model. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.29) March 6 2026 ## mcp-server v0.10.1 MCP Fixed a publishing issue affecting version 0.10.0. **Read more** ## Changes * Resolved a problem that prevented successful publishing with 0.10.0. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.10.1) March 5 2026 ## langchain-glean v0.4.0 langchain-gleanAction required Adds server\_url configuration with instance fallback and updates tooling and SDK usage for improved developer ergonomics. **Read more** ## Action Required * Follow the migration notes before upgrading. ## Changes * Added server\_url support with automatic fallback to the Glean instance. * Migrated task runner from go-task to native mise tasks. ## Source * [Release notes](https://github.com/gleanwork/langchain-glean/releases/tag/v0.4.0) ## glean-indexing-sdk v1.0.0b1 Glean Indexing SDKNoteworthy Glean Indexing SDK now supports configuration via GLEAN\_SERVER\_URL with GLEAN\_INSTANCE fallback and introduces improved error handling and documentation. **Read more** ## Changes * Added support for configuring the SDK using GLEAN\_SERVER\_URL with GLEAN\_INSTANCE as a fallback. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v1.0.0b1) ## glean-agent-toolkit 0.4.0 Glean Agent Toolkit Improves Glean Agent Toolkit’s environment configuration, developer setup, and schema capabilities while fixing adapter hints and lint issues. **Read more** ## Changes * Improves Glean Agent Toolkit’s environment configuration, developer setup, and schema capabilities while fixing adapter hints and lint issues. ## Source * [Release notes](https://github.com/gleanwork/glean-agent-toolkit/releases/tag/0.4.0) ## configure-mcp-server v3.1.0 MCPAction required This release adds server URL configurability for MCP and updates the MCP config package dependency. **Read more** ## Action Required * Follow the migration notes before upgrading. ## Changes * Added CLI flag to configure the MCP server endpoint. * Added environment variable support for MCP server configuration. * Migrated MCP config dependency. ## Source * [Release notes](https://github.com/gleanwork/configure-mcp-server/releases/tag/v3.1.0) February 23 2026 ## mcp-config-schema v4.2.0 MCP No functional changes documented for mcp-config-schema v4.2.0 based on the provided release notes. **Read more** ## Changes * Version identifier updated to mcp-config-schema v4.2.0 for tracking and dependency alignment. ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v4.2.0) ## glean-indexing-sdk v1.0.0b0 Glean Indexing SDK Introduces a custom exception hierarchy for the Glean Indexing SDK and improves release packaging and documentation references. **Read more** ## Changes * Introduces a custom exception hierarchy for the Glean Indexing SDK and improves release packaging and documentation references. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v1.0.0b0) February 20 2026 ## api-client-typescript v0.14.7 API Clients Updated to change the field. **Read more** ## Changes * Category: API Clients - Affected method:. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.7) ## api-client-python v0.12.8 API Clients The response.unauthorized\_datasource\_instances field was changed in glean.authentication.checkdatasourceauth(). **Read more** ## Changes * Review usage of this endpoint for compatibility with the updated response field. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.8) ## api-client-java v0.12.23 API Clients Updated so that has changed in the API client. **Read more** ## Changes * Category: API Clients - Package:. * Change: in response marked as changed. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.23) ## api-client-go v0.11.28 API Clients Updated to change the field. **Read more** ## Changes * Behavior or structure has been modified. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.28) February 19 2026 ## REST API: changes (endpoints) 2026-02-19 APINoteworthy 1 endpoint added. **Read more** ## Changes * Added endpoint: /checkdatasourceauth. * 1 endpoint added. ## api-client-typescript v0.14.6 API Clients Added the method to the package in version 0.14.6. **Read more** ## Changes * Category: API Clients - New:. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.6) ## api-client-python v0.12.7 API Clients Added a new authentication helper for checking data source authorization in glean-api-client 0.12.7. **Read more** ## Changes * Added for data source auth checks. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.7) ## api-client-java v0.12.22 API Clients Added the method to the API client for authentication checks. **Read more** ## Changes * New function added to the client. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.22) ## api-client-go v0.11.27 API Clients Added the method to the Go API client. **Read more** ## Changes * Added to support datasource authentication checks. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.27) February 18 2026 ## api-client-typescript v0.14.5 API ClientsBreaking Breaking changes to response types in glean.client.chat.create() and glean.client.chat.retrieve(); changed messageType on chat messages in glean.client.chat.createStream(). **Read more** ## Changes * Added unauthorizedDatasourceInstances to response and error types in glean.client.search.query(), glean.client.search.queryAsAdmin(), glean.client.search.autocomplete(), glean.client.search.recommendations(), and glean.client.messages.retrieve(). ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.5) ## api-client-python v0.12.6 API Clients Glean-api-client 0.12.6 changes chat message type fields and adds unauthorized datasource error details across search APIs. **Read more** ## Changes * Changed in for and , and in for (breaking). * Changed for (breaking). * Added (and ) to , , , ,. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.6) ## api-client-java v0.12.21 API ClientsBreaking This release updates chat messageType fields and introduces unauthorizedDatasourceInstances error details across search and messages APIs, including breaking changes to chat create and retrieve responses. **Read more** ## Changes * This release updates chat messageType fields and introduces unauthorizedDatasourceInstances error details across search and messages APIs, including breaking changes to chat create and retrieve responses. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.21) ## api-client-go v0.11.26 API Clients Api-client-go 0.11.26 updates chat message type fields and adds UnauthorizedDatasourceInstances error details across messages and search APIs, including several breaking response changes. **Read more** ## Changes * Api-client-go 0.11.26 updates chat message type fields and adds UnauthorizedDatasourceInstances error details across messages and search APIs, including several breaking response changes. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.26) February 10 2026 ## api-client-typescript v0.14.4 API ClientsNoteworthy Added support for the enum value in the activity feedback API request. ## api-client-python v0.12.5 API Clients The request.feedback1.event enum value feedback\_time\_saved was added to glean.client.activity.feedback(). **Read more** ## Changes * Added feedback\_time\_saved enum to activity feedback events. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.5) ## api-client-java v0.12.20 API Clients Added a new feedbackTimeSaved enum value to the feedback request event in glean.client.activity.feedback(). **Read more** ## Changes * Added request.feedback1.event.enum(feedbackTimeSaved) to glean.client.activity.feedback(). ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.20) ## api-client-go v0.11.25 API Clients The request.Feedback1.Event enum value feedbackTimeSaved was added to Glean.Client.Activity.Feedback(). **Read more** ## Changes * Added feedbackTimeSaved enum to activity feedback events. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.25) February 9 2026 ## api-client-typescript v0.14.3 API Clients Added the response.tools field to glean.client.agents.retrieveSchemas() in the @gleanwork/api-client 0.14.3 release. **Read more** ## Changes * Added response.tools to glean.client.agents.retrieveSchemas() output. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.3) ## api-client-python v0.12.4 API Clients Released Python API client v0.12.4 generated from OpenAPI Doc 0.9.0 using Speakeasy CLI 1.709.1 (2.812.2). **Read more** ## Changes * Generated Python SDK version v0.12.4 - Published Python package release v0.12.4. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.4) ## api-client-python v0.12.3 API Clients The response.tools field was added to glean.client.agents.retrieve\_schemas(). **Read more** ## Changes * Added tools field to the agent schema retrieval response. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.3) ## api-client-java v0.12.19 API Clients The method now includes the field in its response. **Read more** ## Changes * Added to the response. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.19) ## api-client-go v0.11.24 API Clients The response.Tools field was added to Glean.Client.Agents.RetrieveSchemas(). **Read more** ## Changes * Added Tools field to the agent schema retrieval response. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.24) February 6 2026 ## api-client-typescript v0.14.1 API Clients Updated the TypeScript API client to version 0.14.1 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Generated TypeScript client v0.14.1 - Aligned with OpenAPI Doc 0.9.0 - Release available as NPM package v0.14.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.14.1) ## api-client-python v0.12.1 API Clients Updated Python API client to version 0.12.1 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Python client generated at v0.12.1 - Released on PyPI as v0.12.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.12.1) ## api-client-java v0.12.16 API Clients Updated Java API client to version 0.12.16 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.706.1 (2.809.2). **Read more** ## Changes * Java client v0.12.16 released to Maven Central. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.16) ## api-client-go v0.11.22 API ClientsBreaking Updated Go API client to version 0.11.22 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * No additional details or breaking changes were specified in this release. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.22) February 5 2026 ## glean-indexing-sdk v0.3.1 Glean Indexing SDKNoteworthy Added a guided release process, standardized async class and file naming, and improved release commit contents. **Read more** ## Changes * Introduced /release command for guided releases - Renamed async classes with Base prefix and files to base\_\* convention - Included CHANGELOG.md and uv.lock in release commits. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v0.3.1) ## glean-indexing-sdk v0.3.0 Glean Indexing SDK Introduces a guided release process, standardizes async class and file naming, and improves Python version management for the Glean Indexing SDK. **Read more** ## Changes * Introduces a guided release process, standardizes async class and file naming, and improves Python version management for the Glean Indexing SDK. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v0.3.0) January 29 2026 ## mcp-config-schema v4.1.1 MCP Restored README documentation files for both mcp-config-glean and mcp-config-schema packages. **Read more** ## Changes * Documentation updates only - No protocol or schema changes - Committer: Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v4.1.1) January 22 2026 ## mcp-config-schema v4.1.0 MCPNoteworthy This release updates the MCP config schema with enhancements to client support, API behavior, and authentication options. **Read more** ## Changes * Added Cursor Agent to clients. * Updated clientNeedsMcpRemote in the API. * Introduced new authentication configurations. ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v4.1.0) January 21 2026 ## langchain-glean v0.3.4 langchain-glean Updated SDK to use vars() for Speakeasy models and removed unused parameters, with improved environment variable propagation. **Read more** ## Changes * Updated SDK to use vars() for Speakeasy models and removed unused parameters, with improved environment variable propagation. ## Source * [Release notes](https://github.com/gleanwork/langchain-glean/releases/tag/v0.3.4) January 16 2026 ## api-client-typescript v0.13.20 API Clients Updated TypeScript API client to version 0.13.20 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.686.0 (2.796.1). **Read more** ## Changes * Updated TypeScript API client to version 0.13.20 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.686.0 (2.796.1). ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.20) ## api-client-python v0.11.27 API Clients Python API client updated to version 0.11.27 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.686.0 (2.796.1). **Read more** ## Changes * Generated python v0.11.27 - Released on PyPI v0.11.27. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.27) ## api-client-java v0.12.14 API Clients Updated Java API client to version 0.12.14 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.686.0 (2.796.1). **Read more** ## Changes * Java client v0.12.14 released - Maven Central v0.12.14 published. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.14) ## api-client-go v0.11.20 API Clients Go API client updated to version 0.11.20 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.686.0 (2.796.1). **Read more** ## Changes * Generated Go client v0.11.20 - Release includes updates from OpenAPI Doc 0.9.0 - Built using Speakeasy CLI 1.686.0 (2.796.1). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.20) January 14 2026 ## mcp-config-schema v4.0.1 MCP Added --bearer-token-env-var flag to Codex CLI for API token authentication and improved CLAUDE.md documentation with architecture and vendor-neutrality details. **Read more** ## Changes * Added --bearer-token-env-var flag to Codex CLI for API token authentication and improved CLAUDE.md documentation with architecture and vendor-neutrality details. ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v4.0.1) ## mcp-config-schema v4.0.0 MCP Tooling configuration consolidated at the monorepo root and converted to an npm workspaces monorepo. **Read more** ## Changes * Affects: mcp-config-glean, mcp-config-schema. * Refactored configuration structure for improved maintainability - Transitioned to npm workspaces for streamlined dependency management. ## Source * [Release notes](https://github.com/gleanwork/mcp-config/releases/tag/v4.0.0) January 10 2026 ## mcp-config-schema v3.1.1 MCP Clients without a configPath now receive null from buildCommand, preventing errors in protocol-level command construction. **Read more** ## Changes * Fixed buildCommand to return null for clients lacking configPath - Ensures protocol-level stability for clients with missing configuration - Committer: Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v3.1.1) January 9 2026 ## configure-mcp-server v3.0.1 MCP Removed the engines field from configuration and restored Node 20 CI testing. **Read more** ## Changes * Internal change only - No impact on external protocol or config schema - Change committed by Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/configure-mcp-server/releases/tag/v3.0.1) January 8 2026 ## mcp-config-schema v3.1.0 MCPAction requiredDeprecated This release adds native HTTP transport support for JetBrains (2025.2+), deprecates getNormalizedServersConfig for future removal, corrects the Junie config path, and introduces config validation for Claude Code commands. **Read more** ## Action Required * Plan migration away from deprecated behavior. ## Changes * This release adds native HTTP transport support for JetBrains (2025.2+), deprecates getNormalizedServersConfig for future removal, corrects the Junie config path, and introduces config validation for Claude Code commands. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v3.1.0) January 7 2026 ## configure-mcp-server v3.0.0 MCPBreakingAction required The minimum supported Node.js version is now 22 LTS, and internal dependencies and documentation have been updated. **Read more** ## Action Required * Verify your runtime uses Node.js 22 or later. ## Changes * Documentation: Added GA stability badge to README. * Internal: Migrated to bintastic and updated config package dependencies. ## Breaking Changes * Breaking: Node.js 22 LTS is now required. ## Source * [Release notes](https://github.com/gleanwork/configure-mcp-server/releases/tag/v3.0.0) January 6 2026 ## mcp-config-schema v3.0.0 MCP Server naming and CLI configuration are now vendor-neutral, with updated terminology and improved type safety in config builders. **Read more** ## Changes * Server naming and CLI configuration are now vendor-neutral, with updated terminology and improved type safety in config builders. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v3.0.0) January 5 2026 ## mcp-config-schema v3.0.0-beta.0 MCP Mcp-config-schema v3.0.0-beta.0. **Read more** ## Changes * Mcp-config-schema v3.0.0-beta.0. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v3.0.0-beta.0) December 16 2025 ## Deep Linked Citations Migration Guide APIDocumentationAction required Added a migration guide for custom chat frontends to adopt deep-linked citations in the `/chat` API response. **Read more** ## Action Required * Follow the migration notes before upgrading. ## Changes * New response format and fields. * Minimal vs full migration paths. * Code examples (TypeScript/React). * Fetching document content for contextual previews. December 12 2025 ## api-client-typescript v0.13.18 API Clients Added export size fields to findings export APIs. **Read more** ## Changes * Glean.governance.createfindingsexport(): response.exportsize added - glean.governance.listfindingsexports(): response.exports.\[].exportSize added. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.18) ## api-client-python v0.11.25 API Clients Added export\_size to the response of glean.governance.createfindingsexport() and to each export in glean.governance.listfindingsexports(). **Read more** ## Changes * Glean.governance.createfindingsexport(): response.export\_size added - glean.governance.listfindingsexports(): response.exports.\[].export\_size added. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.25) ## api-client-java v0.12.12 API Clients Added export size fields to findings export APIs. **Read more** ## Changes * Glean.governance.createfindingsexport(): response.exportsize added - glean.governance.listfindingsexports(): response.exports.\[].exportSize added. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.12) ## api-client-go v0.11.18 API Clients Added ExportSize to the response of Glean.Governance.Createfindingsexport() and to each export in Glean.Governance.Listfindingsexports(). **Read more** ## Changes * Glean.Governance.Createfindingsexport(): response.ExportSize added - Glean.Governance.Listfindingsexports(): response.Exports.\[].ExportSize added. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.18) December 11 2025 ## api-client-typescript v0.13.17 API Clients Updated TypeScript API client to version 0.13.17 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - TypeScript client generated at v0.13.17 - NPM package released at v0.13.17. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.17) ## api-client-python v0.11.24 API Clients Updated Python API client to version 0.11.24 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.675.4 (2.779.2). **Read more** ## Changes * Python client generated at v0.11.24 - Released on PyPI as v0.11.24. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.24) ## api-client-java v0.12.11 API Clients Updated Java API client to version 0.12.11 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Java client generated at v0.12.11 - Released to Maven Central v0.12.11. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.11) ## api-client-go v0.11.17 API ClientsBreaking Updated Go API client to version 0.11.17 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.675.4 (2.779.2). **Read more** ## Changes * No additional details about new endpoints, parameters, or breaking changes were provided in these release notes. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.17) December 10 2025 ## api-client-typescript v0.13.16 API Clients Updated TypeScript API client to version 0.13.16 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Updated TypeScript API client to version 0.13.16 based on OpenAPI Doc 0.9.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.16) ## api-client-typescript v0.13.15 API Clients New governance findings export endpoints were added, and multiple existing endpoints changed request and response fields related to authorship, roles, attribution, and sensitive content options. **Read more** ## Changes * New governance findings export endpoints were added, and multiple existing endpoints changed request and response fields related to authorship, roles, attribution, and sensitive content options. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.15) ## api-client-python v0.11.23 API Clients Api-client-python v0.11.23 includes 6 additions, 51 changes. **Read more** ## Changes * Added `response.report.config.sensitive_content_options.custom_sensitive_expressions` to `client.governance.data.policies.retrieve()`. * Added `request.config.sensitive_content_options.custom_sensitive_expressions` to `client.governance.data.policies.update()`. * Added `response.reports.[].config.sensitive_content_options.custom_sensitive_expressions` to `client.governance.data.policies.list()`. * Added `request.config.sensitive_content_options.custom_sensitive_expressions` to `client.governance.data.policies.create()`. * Added `response.report.config.sensitive_content_options.custom_sensitive_expressions` to `client.governance.data.policies.create()`. * Added `request.config.sensitive_content_options.custom_sensitive_expressions` to `client.governance.data.reports.create()`. * Changed `request.body.structured_list.[].document.metadata.author` on `client.announcements.create()`. * Changed `response.body.structured_list.[].document.metadata.author` on `client.announcements.create()`. * Changed `request.body.structured_list.[].document.metadata.author` on `client.announcements.update()`. * Changed `response.body.structured_list.[].document.metadata.author` on `client.announcements.update()`. * Changed `request.data.added_roles.[].person` on `client.answers.create()`. * Changed `response.added_roles.[].person` on `client.answers.create()`. * Changed `request.added_roles.[].person` on `client.answers.update()`. * Changed `response.added_roles.[].person` on `client.answers.update()`. * Changed `response.answer_result.answer.added_roles.[].person` on `client.answers.retrieve()`. * Changed `response.answer_results.[].answer.added_roles.[].person` on `client.answers.list()`. * Changed `request.messages.[].citations.[].source_document.metadata.author` on `client.chat.create()`. * Changed `response.messages.[].citations.[].source_document.metadata.author` on `client.chat.create()`. * Changed `response.chat_result.chat.created_by` on `client.chat.retrieve()`. * Changed `response.chat_results.[].chat.created_by` on `client.chat.list()`. * Changed `request.messages.[].citations.[].source_document.metadata.author` on `client.chat.create_stream()`. * Changed `response.collection.added_roles.[].person` on `client.collections.add_items()`. * Changed `request.added_roles.[].person` on `client.collections.create()`. * Changed `response.[class].collection.added_roles.[].person` on `client.collections.create()`. * Changed `response.collection.added_roles.[].person` on `client.collections.delete_item()`. * Changed `request.added_roles.[].person` on `client.collections.update()`. * Changed `response.added_roles.[].person` on `client.collections.update()`. * Changed `response.collection.added_roles.[].person` on `client.collections.update_item()`. * Changed `response.collection.added_roles.[].person` on `client.collections.retrieve()`. * Changed `response.collections.[].added_roles.[].person` on `client.collections.list()`. * Changed `response.documents.{}.[document].metadata.author` on `client.documents.retrieve()`. * Changed `response.documents.[].metadata.author` on `client.documents.retrieve_by_facets()`. * Changed `response` on `client.insights.retrieve()`. * Changed `response.search_response.results.[].structured_results.[].document.metadata.author` on `client.messages.retrieve()`. * Changed `response.attribution` on `client.pins.update()`. * Changed `response.pin.attribution` on `client.pins.retrieve()`. * Changed `response.pins.[].attribution` on `client.pins.list()`. * Changed `response.attribution` on `client.pins.create()`. * Changed `request.source_document.metadata.author` on `client.search.query_as_admin()`. * Changed `response.results.[].structured_results.[].document.metadata.author` on `client.search.query_as_admin()`. * Changed `response.results.[].document.metadata.author` on `client.search.autocomplete()`. * Changed `response.results.[].primary_entry.created_by` on `client.search.retrieve_feed()`. * Changed `request.source_document.metadata.author` on `client.search.recommendations()`. * Changed `response.results.[].structured_results.[].document.metadata.author` on `client.search.recommendations()`. * Changed `request.source_document.metadata.author` on `client.search.query()`. * Changed `response.results.[].structured_results.[].document.metadata.author` on `client.search.query()`. * Changed `response.results.[]` on `client.entities.list()`. * Changed `response.results.[]` on `client.entities.read_people()`. * Changed `request.data.added_roles.[].person` on `client.shortcuts.create()`. * Changed `response.shortcut.added_roles.[].person` on `client.shortcuts.create()`. * Changed `response.shortcut.added_roles.[].person` on `client.shortcuts.retrieve()`. * Changed `response.shortcuts.[].added_roles.[].person` on `client.shortcuts.list()`. * Changed `request.added_roles.[].person` on `client.shortcuts.update()`. * Changed `response.shortcut.added_roles.[].person` on `client.shortcuts.update()`. * Changed `response.metadata.last_verifier` on `client.verification.add_reminder()`. * Changed `response.documents.[].metadata.last_verifier` on `client.verification.list()`. * Changed `response.metadata.last_verifier` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.23) ## api-client-java v0.12.9 API Clients Api-client-java v0.12.9 includes 6 additions, 51 changes. **Read more** ## Changes * Added `response.report.config.sensitiveContentOptions.customSensitiveExpressions` to `client.governance.data.policies.retrieve()`. * Added `request.updatedlpreportrequest.config.sensitiveContentOptions.customSensitiveExpressions` to `client.governance.data.policies.update()`. * Added `response.reports.[].config.sensitiveContentOptions.customSensitiveExpressions` to `client.governance.data.policies.list()`. * Added `request.config.sensitiveContentOptions.customSensitiveExpressions` to `client.governance.data.policies.create()`. * Added `response.report.config.sensitiveContentOptions.customSensitiveExpressions` to `client.governance.data.policies.create()`. * Added `request.config.sensitiveContentOptions.customSensitiveExpressions` to `client.governance.data.reports.create()`. * Changed `request.body.structuredList.[].document.metadata.author` on `client.announcements.create()`. * Changed `response.body.structuredList.[].document.metadata.author` on `client.announcements.create()`. * Changed `request.body.structuredList.[].document.metadata.author` on `client.announcements.update()`. * Changed `response.body.structuredList.[].document.metadata.author` on `client.announcements.update()`. * Changed `request.data.addedRoles.[].person` on `client.answers.create()`. * Changed `response.addedroles.[].person` on `client.answers.create()`. * Changed `request.addedRoles.[].person` on `client.answers.update()`. * Changed `response.addedroles.[].person` on `client.answers.update()`. * Changed `response.answerresult.answer.addedRoles.[].person` on `client.answers.retrieve()`. * Changed `response.answerresults.[].answer.addedRoles.[].person` on `client.answers.list()`. * Changed `request.chatrequest.messages.[].citations.[].sourceDocument.metadata.author` on `client.chat.create()`. * Changed `response.messages.[].citations.[].sourceDocument.metadata.author` on `client.chat.create()`. * Changed `response.chatresult.chat.createdBy` on `client.chat.retrieve()`. * Changed `response.chatresults.[].chat.createdBy` on `client.chat.list()`. * Changed `request.chatrequest.messages.[].citations.[].sourceDocument.metadata.author` on `client.chat.createstream()`. * Changed `response.collection.addedRoles.[].person` on `client.collections.additems()`. * Changed `request.addedRoles.[].person` on `client.collections.create()`. * Changed `response.[class].collection.addedRoles.[].person` on `client.collections.create()`. * Changed `response.collection.addedRoles.[].person` on `client.collections.deleteitem()`. * Changed `request.addedRoles.[].person` on `client.collections.update()`. * Changed `response.addedroles.[].person` on `client.collections.update()`. * Changed `response.collection.addedRoles.[].person` on `client.collections.updateitem()`. * Changed `response.collection.addedRoles.[].person` on `client.collections.retrieve()`. * Changed `response.collections.[].addedRoles.[].person` on `client.collections.list()`. * Changed `response.documents.{}.[document].metadata.author` on `client.documents.retrieve()`. * Changed `response.documents.[].metadata.author` on `client.documents.retrievebyfacets()`. * Changed `response` on `client.insights.retrieve()`. * Changed `response.searchresponse.results.[].structuredResults.[].document.metadata.author` on `client.messages.retrieve()`. * Changed `response.attribution` on `client.pins.update()`. * Changed `response.pin.attribution` on `client.pins.retrieve()`. * Changed `response.pins.[].attribution` on `client.pins.list()`. * Changed `response.attribution` on `client.pins.create()`. * Changed `request.sourceDocument.metadata.author` on `client.search.queryasadmin()`. * Changed `response.results.[].structuredResults.[].document.metadata.author` on `client.search.queryasadmin()`. * Changed `response.results.[].document.metadata.author` on `client.search.autocomplete()`. * Changed `response.results.[].primaryEntry.createdBy` on `client.search.retrievefeed()`. * Changed `request.sourceDocument.metadata.author` on `client.search.recommendations()`. * Changed `response.results.[].structuredResults.[].document.metadata.author` on `client.search.recommendations()`. * Changed `request.sourceDocument.metadata.author` on `client.search.query()`. * Changed `response.results.[].structuredResults.[].document.metadata.author` on `client.search.query()`. * Changed `response.results.[]` on `client.entities.list()`. * Changed `response.results.[]` on `client.entities.readpeople()`. * Changed `request.data.addedRoles.[].person` on `client.shortcuts.create()`. * Changed `response.shortcut.addedRoles.[].person` on `client.shortcuts.create()`. * Changed `response.shortcut.addedRoles.[].person` on `client.shortcuts.retrieve()`. * Changed `response.shortcuts.[].addedRoles.[].person` on `client.shortcuts.list()`. * Changed `request.addedRoles.[].person` on `client.shortcuts.update()`. * Changed `response.shortcut.addedRoles.[].person` on `client.shortcuts.update()`. * Changed `response.metadata.lastVerifier` on `client.verification.addreminder()`. * Changed `response.documents.[].metadata.lastVerifier` on `client.verification.list()`. * Changed `response.metadata.lastVerifier` on `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.9) ## api-client-java v0.12.10 API Clients Updated Java API client to version 0.12.10 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.675.2. **Read more** ## Changes * Java client v0.12.10 generated - Released to Maven Central v0.12.10. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.10) ## api-client-go v0.11.16 API ClientsBreaking Updated Go API client to version 0.11.16 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.675.2 (2.778.5). **Read more** ## Changes * No additional details or breaking changes were specified in the release notes. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.16) ## api-client-go v0.11.15 API Clients New governance findings export endpoints were added, and multiple request and response fields were changed across Announcements, Answers, Chat, Collections, Documents, Pins, Search, Entities, Shortcuts, and Verification APIs. **Read more** ## Changes * New governance findings export endpoints were added, and multiple request and response fields were changed across Announcements, Answers, Chat, Collections, Documents, Pins, Search, Entities, Shortcuts, and Verification APIs. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.15) December 9 2025 ## mcp-config-schema v2.0.1 MCP Fixed an issue with the windsurf manual configuration. **Read more** ## Changes * Bug fix for windsurf manual configuration. * Committers: David J. Hamilton, Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v2.0.1) December 4 2025 ## REST API: changes (endpoints) 2025-12-04 APIBreakingAction requiredDeprecated Breaking: 2 endpoints removed. **Read more** ## Action Required * Update callers to stop using /custom-metadata/schema/{groupName}. * Update callers to stop using /document/{docId}/custom-metadata/{groupName}. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking: 2 endpoints removed. * Removed endpoint: /custom-metadata/schema/{groupName}. * Removed endpoint: /document/{docId}/custom-metadata/{groupName}. December 1 2025 ## mcp-server v0.9.1 MCP V0.9.0 was re-released with no additional protocol-level or config schema changes. **Read more** ## Changes * V0.9.0 was re-released with no additional protocol-level or config schema changes. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.9.1) November 26 2025 ## REST API: changes (endpoints) 2025-11-26 APINoteworthy 2 endpoints added. **Read more** ## Changes * Added endpoint: /custom-metadata/schema/{groupName}. * Added endpoint: /document/{docId}/custom-metadata/{groupName}. * 2 endpoints added. November 25 2025 ## configure-mcp-server v2.1.0 MCP Upgraded mcp-config-schema to version 2.0.0. **Read more** ## Changes * Enhancement: Updated dependency to mcp-config-schema 2.0.0. * Internal: Added CLAUDE.md. ## Source * [Release notes](https://github.com/gleanwork/configure-mcp-server/releases/tag/v2.1.0) November 24 2025 ## api-client-typescript v0.13.14 API ClientsBreaking The response.agentsresponse.agentsUsageByDepartmentInsights array in glean.client.insights.retrieve() has changed. **Read more** ## Changes * Review integration for possible breaking changes in this response structure. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.14) ## api-client-python v0.11.22 API Clients The response.agents\_response.agents\_usage\_by\_department\_insights array in glean.client.insights.retrieve() has changed. **Read more** ## Changes * The response.agents\_response.agents\_usage\_by\_department\_insights array in glean.client.insights.retrieve() has changed. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.22) ## api-client-java v0.12.8 API ClientsBreaking The response.agentsresponse.agentsUsageByDepartmentInsights array in glean.client.insights.retrieve() has changed. **Read more** ## Changes * Review integration for potential breaking changes in this response structure. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.8) ## api-client-go v0.11.14 API Clients Changed the structure of response.AgentsResponse.AgentsUsageByDepartmentInsights\[] in Glean.Client.Insights.Retrieve(). **Read more** ## Changes * Changed the structure of response.AgentsResponse.AgentsUsageByDepartmentInsights\[] in Glean.Client.Insights.Retrieve(). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.14) November 21 2025 ## api-client-typescript v0.13.13 API Clients Updated TypeScript API client to version 0.13.13 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.660.0. **Read more** ## Changes * TypeScript client generated at v0.13.13 - Release available as NPM package v0.13.13. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.13) ## api-client-typescript v0.13.12 API Clients Updated TypeScript API client to version 0.13.12 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.660.0. **Read more** ## Changes * Updated TypeScript API client to version 0.13.12 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.660.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.12) ## api-client-typescript v0.13.11 API ClientsBreakingAction requiredDeprecated The response structure of glean.client.insights.retrieve() has changed in a breaking way. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change: applications using glean.client.insights.retrieve() may require updates to handle the new response format. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change: applications using glean.client.insights.retrieve() may require updates to handle the new response format. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.11) ## api-client-python v0.11.20 API Clients Updated Python API client to version 0.11.20 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Updated Python API client to version 0.11.20 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.20) ## api-client-python v0.11.18 API Clients Updated Python API client to version 0.11.18, based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Python SDK generated at v0.11.18 - PyPI release published at v0.11.18 - Based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.18) ## api-client-python v0.11.16 API ClientsBreakingAction requiredDeprecated The response structure for glean.client.insights.retrieve() has changed in a breaking way. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change: applications using this method may require updates to handle the new response format. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change: applications using this method may require updates to handle the new response format. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.16) ## api-client-java v0.12.7 API Clients Updated Java API client to version 0.12.7 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.660.0. **Read more** ## Changes * Java SDK generated at v0.12.7 - Released to Maven Central v0.12.7. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.7) ## api-client-java v0.12.6 API Clients Updated Java API client to version 0.12.6 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.660.0. **Read more** ## Changes * Java SDK generated at v0.12.6 - Released to Maven Central v0.12.6. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.6) ## api-client-java v0.12.5 API ClientsBreaking The response object returned by glean.client.insights.retrieve() has changed in a breaking way. **Read more** ## Changes * This is a breaking change and may require updates to client code using this method. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.5) ## api-client-go v0.11.13 API Clients Updated Go API client to version 0.11.13 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.660.0 (2.760.2). **Read more** ## Changes * Reflects changes from the latest OpenAPI specification - Incorporates updates from recent Speakeasy CLI versions. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.13) ## api-client-go v0.11.12 API Clients Updated Go API client to version 0.11.12 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.660.0 (2.760.2). **Read more** ## Changes * Reflects changes from the latest OpenAPI specification - Incorporates updates from the latest Speakeasy CLI release. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.12) ## api-client-go v0.11.11 API ClientsBreakingAction requiredDeprecated The response object returned by Glean.Client.Insights.Retrieve() has changed in a breaking way. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change to the response structure - Review integration for compatibility before upgrading. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change to the response structure - Review integration for compatibility before upgrading. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.11) November 18 2025 ## mcp-config-schema v2.0.0 MCPNoteworthy Renamed components for improved semantic clarity, added support for Jetbrains IDEs and Codex, and updated documentation to reflect recent changes. **Read more** ## Changes * Renamed components for improved semantic clarity, added support for Jetbrains IDEs and Codex, and updated documentation to reflect recent changes. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v2.0.0) November 17 2025 ## api-client-typescript v0.13.10 API Clients Updated API client to typescript v0.13.10 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Updated API client to typescript v0.13.10 based on OpenAPI Doc 0.9.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.10) ## api-client-python v0.11.14 API Clients Updated Python API client to version 0.11.14 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Updated Python API client to version 0.11.14 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.14) ## api-client-java v0.12.4 API Clients Updated Java API client to version 0.12.4 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.658.1. **Read more** ## Changes * Java client v0.12.4 generated - Released to Maven Central v0.12.4. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.4) ## api-client-go v0.11.10 API Clients Updated Go API client to v0.11.10 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.658.1 (2.755.6). **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - Generated using Speakeasy CLI 1.658.1 (2.755.6) - Go API client version updated to v0.11.10. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.10) November 14 2025 ## api-client-typescript v0.13.9 API Clients Updated TypeScript API client to version 0.13.9 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.657.0. **Read more** ## Changes * Includes all changes from OpenAPI Doc 0.9.0 - Generated TypeScript client at v0.13.9 - Released as NPM package v0.13.9. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.9) ## api-client-typescript v0.13.8 API Clients Updated TypeScript API client to version 0.13.8, generated from OpenAPI Doc 0.9.0 using Speakeasy CLI 1.657.0. **Read more** ## Changes * Includes all changes from OpenAPI Doc 0.9.0 - TypeScript SDK now at v0.13.8 - NPM package released as v0.13.8. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.8) ## api-client-typescript v0.13.7 API ClientsBreakingAction requiredDeprecated The request.getchatfilesrequest.chatId parameter was removed from glean.client.chat.retrieveFiles(), introducing a breaking change. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change: chatId parameter is no longer accepted in retrieveFiles() - Update any integrations relying on chatId in this method. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change: chatId parameter is no longer accepted in retrieveFiles() - Update any integrations relying on chatId in this method. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.7) ## api-client-python v0.11.8 API ClientsBreakingDeprecated In glean 0.11.8, the request.chat\_id parameter was removed from glean.client.chat.retrieve\_files(), introducing a breaking change. **Read more** ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking: request.chat\_id is no longer accepted in retrieve\_files() - Update any integrations that relied on this parameter to avoid errors. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.8) ## api-client-python v0.11.12 API Clients Updated Python API client to version 0.11.12 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Python client generated at v0.11.12 - PyPI release published at v0.11.12. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.12) ## api-client-python v0.11.10 API Clients Updated Python API client to version 0.11.10 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Python SDK generated at v0.11.10 - Released to PyPI as v0.11.10. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.10) ## api-client-java v0.12.3 API Clients Updated Java API client to version 0.12.3 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.657.0. **Read more** ## Changes * Java SDK v0.12.3 generated - Released to Maven Central v0.12.3. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.3) ## api-client-java v0.12.2 API ClientsBreakingAction requiredDeprecated The chatId field was removed from request.getchatfilesrequest in glean.client.chat.retrieveFiles(), introducing a breaking change. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change: chatId parameter is no longer available in retrieveFiles() requests. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change: chatId parameter is no longer available in retrieveFiles() requests. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.2) ## api-client-go v0.11.9 API Clients Updated Go API client to v0.11.9 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.657.0 (2.755.0). **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - Generated using Speakeasy CLI 1.657.0 (2.755.0) - Go API client version updated to v0.11.9. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.9) ## api-client-go v0.11.8 API Clients Updated Go API client to version 0.11.8 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.657.0. **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - Generated using Speakeasy CLI 1.657.0 - Go client now at version 0.11.8. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.8) ## api-client-go v0.11.7 API ClientsBreakingDeprecated The ChatId field has been removed from request.GetChatFilesRequest in Glean.Client.Chat.RetrieveFiles(), introducing a breaking change. **Read more** ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking: request.GetChatFilesRequest.ChatId is no longer available - Update code to remove usage of the ChatId field in RetrieveFiles() requests. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.7) November 13 2025 ## api-client-typescript v0.13.6 API Clients Updated TypeScript API client to version 0.13.6 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.656.1 (2.753.1). **Read more** ## Changes * Includes all changes from OpenAPI Doc 0.9.0 - Generated TypeScript client at v0.13.6 - Released as NPM package v0.13.6. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.6) ## api-client-python v0.11.6 API Clients Updated Python API client to version 0.11.6 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Python SDK generated at v0.11.6 - PyPI release v0.11.6. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.6) ## api-client-java v0.12.1 API Clients Updated Java API client to version 0.12.1 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.656.1. **Read more** ## Changes * Java client v0.12.1 generated - Released to Maven Central v0.12.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.1) ## api-client-go v0.11.6 API Clients Updated Go API client to version 0.11.6 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.656.1 (2.753.1). **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - Generated using Speakeasy CLI 1.656.1 (2.753.1) - Go client now at version 0.11.6. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.6) November 11 2025 ## api-client-typescript v0.13.5 API Clients Updated TypeScript API client to v0.13.5 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * * Released as NPM package v0.13.5. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.5) ## api-client-java v0.12.0 API Clients Released java API client v0.12.0 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.653.0 (2.748.0). **Read more** ## Changes * Published to Maven Central as v0.12.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.12.0) ## api-client-go v0.11.5 API Clients Updated Go API client to version 0.11.5 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.653.0 (2.748.0). **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - Incorporates updates from Speakeasy CLI 1.653.0 (2.748.0) - Go client now at version 0.11.5. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.5) November 7 2025 ## api-client-typescript v0.13.4 API Clients Updated the TypeScript API client to version 0.13.4 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * TypeScript client regenerated to reflect latest API specification - NPM package released as version 0.13.4. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.4) ## api-client-python v0.11.4 API Clients Updated Python API client to version 0.11.4 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Python client generated at v0.11.4 - Released on PyPI as v0.11.4. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.4) ## api-client-java v0.11.4 API Clients Updated Java API client to version 0.11.4 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Java client v0.11.4 released to Maven Central - Aligned with OpenAPI Doc 0.9.0 specification - Built using Speakeasy CLI 1.650.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.11.4) ## api-client-go v0.11.4 API Clients The Go API client was updated to version 0.11.4 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.650.0. **Read more** ## Changes * Updated Go client to v0.11.4 - Reflects changes from OpenAPI Doc 0.9.0 - Built using Speakeasy CLI 1.650.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.4) November 5 2025 ## mcp-config-schema v1.0.0 MCPBreakingAction required Updates Windsurf configuration to support native HTTP. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change: protocol-level or configuration schema update for Windsurf. ## Changes * Enhancement: improved native HTTP support in Windsurf configuration. ## Breaking Changes * Breaking change: protocol-level or configuration schema update for Windsurf. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v1.0.0) ## api-client-typescript v0.13.3 API Clients Updated TypeScript API client to version 0.13.3 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.649.0. **Read more** ## Changes * Generated new TypeScript client version 0.13.3 - Release available as NPM package version 0.13.3. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.3) ## api-client-python v0.11.3 API Clients Updated Python API client to version 0.11.3 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.649.0. **Read more** ## Changes * Python SDK generated at v0.11.3 - Released to PyPI as v0.11.3. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.3) ## api-client-java v0.11.3 API Clients Updated Java API client to version 0.11.3 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.649.0. **Read more** ## Changes * Java client v0.11.3 generated - Released to Maven Central v0.11.3. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.11.3) ## api-client-go v0.11.3 API Clients Updated Go API client to version 0.11.3 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.649.0. **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - Generated using Speakeasy CLI 1.649.0 - Go client now at version 0.11.3. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.3) October 29 2025 ## api-client-typescript v0.13.2 API Clients The response.agentsresponse field in glean.client.insights.retrieve() was changed. **Read more** ## Changes * Applies to @gleanwork/api-client version 0.13.2 - Review usage of response.agentsresponse for compatibility. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.2) ## api-client-python v0.11.2 API Clients The response.agents\_response field in glean.client.insights.retrieve() has changed. **Read more** ## Changes * Review integrations using this field for compatibility - No other changes noted in this release. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.2) ## api-client-java v0.11.2 API Clients The response.agentsresponse field in glean.client.insights.retrieve() was changed. **Read more** ## Changes * API response structure for glean.client.insights.retrieve() updated - Review integration for compatibility with the new agentsresponse format. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.11.2) ## api-client-go v0.11.2 API ClientsBreakingAction requiredDeprecatedNoteworthy The response type for Glean.Client.Insights.Retrieve() has changed to response.AgentsResponse. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change to the Insights.Retrieve() method response type - Review integrations using Insights.Retrieve() for compatibility - No new endpoints or parameters introduced in this release. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change to the Insights.Retrieve() method response type - Review integrations using Insights.Retrieve() for compatibility - No new endpoints or parameters introduced in this release. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.2) October 28 2025 ## api-client-typescript v0.13.1 API ClientsNoteworthy Added support for the .chat field in relatedDocuments.structuredResults across multiple API endpoints and updated the response structure for glean.client.documents.retrieve(). **Read more** ## Changes * Added support for the .chat field in relatedDocuments.structuredResults across multiple API endpoints and updated the response structure for glean.client.documents.retrieve(). ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.1) ## api-client-java v0.11.1 API ClientsNoteworthy Added support for the .chat field in relatedDocuments structured results across multiple API endpoints, and changed the response structure for glean.client.documents.retrieve(). **Read more** ## Changes * Added support for the .chat field in relatedDocuments structured results across multiple API endpoints, and changed the response structure for glean.client.documents.retrieve(). ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.11.1) ## api-client-go v0.11.1 API Clients Api-client-go v0.11.1 includes 50 additions, 1 change. **Read more** ## Changes * Added `request.Request.Body.StructuredList.[].Document.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.announcements.create()`. * Added `response.Body.StructuredList.[].Document.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.announcements.create()`. * Added `request.Request.Body.StructuredList.[].Document.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.announcements.update()`. * Added `response.Body.StructuredList.[].Document.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.announcements.update()`. * Added `request.Request.Data.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.answers.create()`. * Added `response.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.answers.create()`. * Added `request.Request.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.answers.update()`. * Added `response.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.answers.update()`. * Added `response.AnswerResult.Answer.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.answers.retrieve()`. * Added `response.AnswerResults.[].Answer.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.answers.list()`. * Added `request.ChatRequest.Messages.[].Citations.[].SourceDocument.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.chat.create()`. * Added `response.Messages.[].Citations.[].SourceDocument.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.chat.create()`. * Added `response.ChatResult.Chat.CreatedBy.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.chat.retrieve()`. * Added `response.ChatResults.[].Chat.CreatedBy.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.chat.list()`. * Added `request.ChatRequest.Messages.[].Citations.[].SourceDocument.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.chat.createstream()`. * Added `response.Collection.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.additems()`. * Added `request.Request.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.create()`. * Added `response.[class].Collection.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.create()`. * Added `response.Collection.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.deleteitem()`. * Added `request.Request.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.update()`. * Added `response.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.update()`. * Added `response.Collection.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.updateitem()`. * Added `response.Collection.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.retrieve()`. * Added `response.Collections.[].AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.collections.list()`. * Added `response.Documents.[].Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.documents.retrievebyfacets()`. * Added `response.Users.ActivityInsights.[].User.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.insights.retrieve()`. * Added `response.SearchResponse.Results.[].StructuredResults.[].Chat` to `client.messages.retrieve()`. * Added `response.Attribution.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.pins.update()`. * Added `response.Pin.Attribution.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.pins.retrieve()`. * Added `response.Pins.[].Attribution.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.pins.list()`. * Added `response.Attribution.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.pins.create()`. * Added `request.Request.SourceDocument.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.search.queryasadmin()`. * Added `response.Results.[].StructuredResults.[].Chat` to `client.search.queryasadmin()`. * Added `response.Results.[].Document.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.search.autocomplete()`. * Added `response.Results.[].PrimaryEntry.CreatedBy.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.search.retrievefeed()`. * Added `request.Request.SourceDocument.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.search.recommendations()`. * Added `response.Results.[].StructuredResults.[].Chat` to `client.search.recommendations()`. * Added `request.Request.SourceDocument.Metadata.Author.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.search.query()`. * Added `response.Results.[].StructuredResults.[].Chat` to `client.search.query()`. * Added `response.Results.[].RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.entities.list()`. * Added `response.Results.[].RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.entities.readpeople()`. * Added `request.Request.Data.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.shortcuts.create()`. * Added `response.Shortcut.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.shortcuts.create()`. * Added `response.Shortcut.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.shortcuts.retrieve()`. * Added `response.Shortcuts.[].AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.shortcuts.list()`. * Added `request.Request.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.shortcuts.update()`. * Added `response.Shortcut.AddedRoles.[].Person.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.shortcuts.update()`. * Added `response.Metadata.LastVerifier.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.verification.addreminder()`. * Added `response.Documents.[].Metadata.LastVerifier.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.verification.list()`. * Added `response.Metadata.LastVerifier.RelatedDocuments.[].Results.[].StructuredResults.[].Chat` to `client.verification.verify()`. * Changed `response.Documents.{}` on `client.documents.retrieve()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.1) October 26 2025 ## configure-mcp-server v2.0.0 MCP The CLI now uses commander.js instead of meow, improving subcommand support and updating documentation for accuracy. **Read more** ## Changes * Refactored CLI to use commander.js for enhanced subcommand handling. * Updated README.md for accurate information. * Improved init help documentation to match current Claude output. ## Source * [Release notes](https://github.com/gleanwork/configure-mcp-server/releases/tag/v2.0.0) October 25 2025 ## mcp-config-schema v0.14.0 MCP Moved zod to peerDependencies to support both v3 and v4, and updated documentation links for supported hosts. **Read more** ## Changes * Moved zod to peerDependencies to support both v3 and v4, and updated documentation links for supported hosts. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.14.0) October 22 2025 ## api-client-typescript v0.13.0 API ClientsNoteworthy Added support for the useImageGeneration field in chat message agentConfig for create, retrieve, and createStream chat API methods. **Read more** ## Changes * Added support for the useImageGeneration field in chat message agentConfig for create, retrieve, and createStream chat API methods. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.13.0) ## api-client-python v0.11.0 API ClientsNoteworthy Added support for the use\_image\_generation field in agent\_config for chat creation, retrieval, and streaming APIs. **Read more** ## Changes * Added support for the use\_image\_generation field in agent\_config for chat creation, retrieval, and streaming APIs. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.11.0) ## api-client-java v0.11.0 API ClientsNoteworthy Added support for the useImageGeneration field in chat message agentConfig for create, retrieve, and createStream chat API methods. **Read more** ## Changes * Added support for the useImageGeneration field in chat message agentConfig for create, retrieve, and createStream chat API methods. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.11.0) ## api-client-go v0.11.0 API ClientsNoteworthy Added support for the UseImageGeneration field in chat message agent configuration for create, retrieve, and stream chat operations. **Read more** ## Changes * Added support for the UseImageGeneration field in chat message agent configuration for create, retrieve, and stream chat operations. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.11.0) October 16 2025 ## api-client-typescript v0.12.3 API ClientsBreakingAction requiredDeprecated The request.datasource parameter in glean.client.messages.retrieve() was changed. This is a breaking change. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change to request.datasource in messages.retrieve() - Review usage of request.datasource to ensure compatibility. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change to request.datasource in messages.retrieve() - Review usage of request.datasource to ensure compatibility. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.12.3) ## api-client-python v0.10.3 API ClientsBreakingAction requiredDeprecated The request.datasource parameter in glean.client.messages.retrieve() has changed, introducing a breaking change. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change to request.datasource in messages.retrieve() - Review usage of request.datasource to ensure compatibility - Update client integrations as needed. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change to request.datasource in messages.retrieve() - Review usage of request.datasource to ensure compatibility - Update client integrations as needed. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.10.3) ## api-client-java v0.10.7 API ClientsBreakingAction requiredDeprecated The request.datasource parameter in glean.client.messages.retrieve() was changed, introducing a breaking change. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change to request.datasource in retrieve() method - Review usage of request.datasource to ensure compatibility. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change to request.datasource in retrieve() method - Review usage of request.datasource to ensure compatibility. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.10.7) ## api-client-go v0.10.3 API ClientsBreakingAction requiredDeprecated The Datasource field in request.Request for Glean.Client.Messages.Retrieve() was changed, introducing a breaking change. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change: request.Request.Datasource modified in Glean.Client.Messages.Retrieve() - Review usage of Datasource in your integration for compatibility. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change: request.Request.Datasource modified in Glean.Client.Messages.Retrieve() - Review usage of Datasource in your integration for compatibility. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.10.3) October 10 2025 ## api-client-typescript v0.12.2 API ClientsBreakingNoteworthy The response.results.\[].primaryEntry.digest.sections.\[].updates.\[].urls field was added to the output of glean.client.search.retrieveFeed(). **Read more** ## Changes * New urls field now available within updates in the retrieveFeed() response - No breaking changes introduced - No changes to authentication or pagination. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.12.2) ## api-client-python v0.10.2 API ClientsBreakingNoteworthy Added the urls field to response.results.\[].primary\_entry.digest.sections.\[].updates in glean.client.search.retrieve\_feed(). **Read more** ## Changes * New urls field now available in the updates objects of the API response - No breaking changes introduced - Applies to glean 0.10.2 API client. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.10.2) ## api-client-java v0.10.6 API Clients A new field, response.results.\[].primaryEntry.digest.sections.\[].updates.\[].urls, was added to the output of glean.client.search.retrieveFeed(). **Read more** ## Changes * Added urls field to updates within digest sections in retrieveFeed() response. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.10.6) ## api-client-go v0.10.2 API ClientsBreakingNoteworthy Added the Urls field to response.Results.\[].PrimaryEntry.Digest.Sections.\[].Updates in Glean.Client.Search.RetrieveFeed(). **Read more** ## Changes * Enables access to URLs within update sections of feed results - No breaking changes introduced - Applies to API client version 0.10.2. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.10.2) October 9 2025 ## api-client-typescript v0.12.1 API ClientsBreakingNoteworthy The response.agentsresponse.topAgentsInsights.\[].icon field was added to the output of glean.client.insights.retrieve(). **Read more** ## Changes * New icon field now available in topAgentsInsights response - No breaking changes introduced - Applies to @gleanwork/api-client version 0.12.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.12.1) ## api-client-python v0.10.1 API Clients The response.agents\_response.top\_agents\_insights array in glean.client.insights.retrieve() now includes the icon field. **Read more** ## Changes * Added icon field to top\_agents\_insights in API response. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.10.1) ## api-client-java v0.10.5 API ClientsBreakingNoteworthy The response.agentsresponse.topAgentsInsights.\[].icon field was added to glean.client.insights.retrieve(). **Read more** ## Changes * New response field: icon in topAgentsInsights array - No breaking changes or new endpoints introduced. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.10.5) ## api-client-go v0.10.1 API ClientsBreakingNoteworthy The Icon field was added to response.AgentsResponse.TopAgentsInsights in Glean.Client.Insights.Retrieve(). **Read more** ## Changes * New Icon field now available in TopAgentsInsights response - No breaking changes introduced - Enhances agent insights data with icon information. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.10.1) October 7 2025 ## REST API: changes (endpoints) 2025-10-07 APIBreakingAction requiredDeprecated Breaking: 1 endpoint removed. **Read more** ## Action Required * Update callers to stop using /api/index/v1/debug/{datasource}/document/events. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking: 1 endpoint removed. * Removed endpoint: /api/index/v1/debug/{datasource}/document/events. ## api-client-typescript v0.12.0 API Clients The response object returned by glean.client.insights.retrieve() has changed. **Read more** ## Changes * Review integration code for compatibility with the updated response structure. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.12.0) ## api-client-python v0.10.0 API Clients The response of glean.client.insights.retrieve() has changed. **Read more** ## Changes * Review integrations using glean.client.insights.retrieve() for compatibility - Update client code if relying on previous response structure. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.10.0) ## api-client-java v0.10.4 API ClientsBreakingNoteworthy The response of glean.client.insights.retrieve() has changed. **Read more** ## Changes * Review integration for possible breaking changes in the response structure. * No new endpoints or parameters introduced. * Check for updates in response handling logic. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.10.4) ## api-client-go v0.10.0 API Clients The response object returned by Glean.Client.Insights.Retrieve() has changed. **Read more** ## Changes * Review integrations using this method for compatibility - Update client code to handle the new response structure if needed. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.10.0) October 1 2025 ## REST API: changes (endpoints) 2025-10-01 APIBreakingAction requiredNoteworthy Breaking: 2 endpoints added, 3 endpoints removed. **Read more** ## Action Required * Update callers to stop using /debug/{datasource}/document/events. * Update callers to stop using /update/{datasource}/extenddeletionpaused. * Update callers to stop using /update/{datasource}/resolvedeletionpaused. ## Changes * Added endpoint: /update/{datasource}/extenddeletionpaused. * Added endpoint: /update/{datasource}/resolvedeletionpaused. ## Breaking Changes * Breaking: 2 endpoints added, 3 endpoints removed. * Removed endpoint: /debug/{datasource}/document/events. * Removed endpoint: /update/{datasource}/extenddeletionpaused. * Removed endpoint: /update/{datasource}/resolvedeletionpaused. ## api-client-typescript v0.11.2 API Clients Updated TypeScript API client to version 0.11.2 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Includes latest changes from OpenAPI specification 0.9.0 - TypeScript SDK now at v0.11.2 - NPM package released as v0.11.2. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.11.2) ## api-client-typescript v0.11.1 API Clients Api-client-typescript v0.11.1 includes 51 additions. **Read more** ## Changes * Added `request.body.structuredList.[].document.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.announcements.create()`. * Added `response.body.structuredList.[].document.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.announcements.create()`. * Added `request.body.structuredList.[].document.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.announcements.update()`. * Added `response.body.structuredList.[].document.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.announcements.update()`. * Added `request.data.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.answers.create()`. * Added `response.addedroles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.answers.create()`. * Added `request.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.answers.update()`. * Added `response.addedroles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.answers.update()`. * Added `response.answerresult.answer.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.answers.retrieve()`. * Added `response.answerresults.[].answer.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.answers.list()`. * Added `request.chatrequest.messages.[].citations.[].sourceDocument.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.chat.create()`. * Added `response.messages.[].citations.[].sourceDocument.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.chat.create()`. * Added `response.chatresult.chat.createdBy.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.chat.retrieve()`. * Added `response.chatresults.[].chat.createdBy.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.chat.list()`. * Added `request.chatrequest.messages.[].citations.[].sourceDocument.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.chat.createstream()`. * Added `response.collection.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.additems()`. * Added `request.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.create()`. * Added `response.[class].collection.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.create()`. * Added `response.collection.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.deleteitem()`. * Added `request.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.update()`. * Added `response.addedroles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.update()`. * Added `response.collection.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.updateitem()`. * Added `response.collection.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.retrieve()`. * Added `response.collections.[].addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.collections.list()`. * Added `response.documents.{}.[document].metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.documents.retrieve()`. * Added `response.documents.[].metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.documents.retrievebyfacets()`. * Added `response.users.activityInsights.[].user.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.insights.retrieve()`. * Added `response.searchresponse.results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.messages.retrieve()`. * Added `response.attribution.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.pins.update()`. * Added `response.pin.attribution.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.pins.retrieve()`. * Added `response.pins.[].attribution.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.pins.list()`. * Added `response.attribution.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.pins.create()`. * Added `request.sourceDocument.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.search.queryasadmin()`. * Added `response.results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.search.queryasadmin()`. * Added `response.results.[].document.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.search.autocomplete()`. * Added `response.results.[].primaryEntry.createdBy.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.search.retrievefeed()`. * Added `request.sourceDocument.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.search.recommendations()`. * Added `response.results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.search.recommendations()`. * Added `request.sourceDocument.metadata.author.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.search.query()`. * Added `response.results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.search.query()`. * Added `response.results.[].relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.entities.list()`. * Added `response.results.[].relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.entities.readpeople()`. * Added `request.data.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.shortcuts.create()`. * Added `response.shortcut.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.shortcuts.retrieve()`. * Added `response.shortcuts.[].addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.shortcuts.list()`. * Added `request.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.shortcuts.update()`. * Added `response.shortcut.addedRoles.[].person.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.shortcuts.update()`. * Added `response.metadata.lastVerifier.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.verification.addreminder()`. * Added `response.documents.[].metadata.lastVerifier.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.verification.list()`. * Added `response.metadata.lastVerifier.relatedDocuments.[].results.[].structuredResults.[].generatedQna.followupActions.[].parameters` to `client.verification.verify()`. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.11.1) ## api-client-python v0.9.3 API Clients Updated Python API client to version 0.9.3 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Python SDK generated at v0.9.3 - PyPI release published at v0.9.3. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.9.3) ## api-client-python v0.9.2 API Clients A new 'parameters' field was added to multiple request and response objects across many glean client API methods. **Read more** ## Changes * A new 'parameters' field was added to multiple request and response objects across many glean client API methods. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.9.2) ## api-client-java v0.10.3 API Clients Updated Java API client to version 0.10.3 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Java SDK generated at v0.10.3 - Released to Maven Central as v0.10.3. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.10.3) ## api-client-java v0.10.2 API Clients Added new parameters field to followupActions in multiple API request and response objects across announcements, answers, chat, collections, documents, insights, messages, pins, search, entities, shortcuts, and verification endpoints. **Read more** ## Changes * Added new parameters field to followupActions in multiple API request and response objects across announcements, answers, chat, collections, documents, insights, messages, pins, search, entities, shortcuts, and verification endpoints. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.10.2) ## api-client-go v0.9.5 API Clients Updated Go API client to version 0.9.5 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.633.1 (2.720.1). **Read more** ## Changes * Updated Go API client to version 0.9.5 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.633.1 (2.720.1). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.9.5) ## api-client-go v0.9.4 API Clients Added the FollowupActions.Parameters field to multiple request and response objects across Announcements, Answers, Chat, Collections, Documents, Insights, Messages, Pins, Search, Entities, Shortcuts, and Verification API methods. **Read more** ## Changes * Added the FollowupActions.Parameters field to multiple request and response objects across Announcements, Answers, Chat, Collections, Documents, Insights, Messages, Pins, Search, Entities, Shortcuts, and Verification API methods. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.9.4) September 30 2025 ## REST API: changes (endpoints) 2025-09-30 APINoteworthy 1 endpoint added. **Read more** ## Changes * Added endpoint: /api/index/v1/debug/{datasource}/document/events. * 1 endpoint added. ## api-client-typescript v0.11.0 API Clients Updated TypeScript API client to version 0.11.0 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Includes all changes from OpenAPI Doc 0.9.0 - - Published as NPM package v0.11.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.11.0) ## api-client-typescript v0.10.5 API ClientsBreakingAction requiredDeprecated The request parameter in glean.client.insights.retrieve() was changed. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change to the request parameter or object - Review usage of glean.client.insights.retrieve() for compatibility. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change to the request parameter or object - Review usage of glean.client.insights.retrieve() for compatibility. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.10.5) ## api-client-python v0.9.1 API Clients Updated Python API client to v0.9.1 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.631.5 (2.716.16). **Read more** ## Changes * Python client generated at version 0.9.1 - Released on PyPI as v0.9.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.9.1) ## api-client-java v0.10.1 API Clients Updated Java API client to version 0.10.1 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.631.5 (2.716.16). **Read more** ## Changes * Java client v0.10.1 generated - Released to Maven Central v0.10.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.10.1) ## api-client-go v0.9.3 API Clients Updated Go API client to v0.9.3 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - Go client generated at version v0.9.3. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.9.3) September 29 2025 ## api-client-python v0.9.0 API Clients The request parameter for glean.client.insights.retrieve() was changed. **Read more** ## Changes * Review usage of glean.client.insights.retrieve() to ensure compatibility with the updated request parameter. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.9.0) ## api-client-java v0.10.0 API ClientsBreakingAction requiredDeprecated The request structure for glean.client.insights.retrieve() was changed. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change to the request parameters for glean.client.insights.retrieve() - Review usage of glean.client.insights.retrieve() to ensure compatibility. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change to the request parameters for glean.client.insights.retrieve() - Review usage of glean.client.insights.retrieve() to ensure compatibility. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.10.0) ## api-client-go v0.9.2 API ClientsBreakingAction requiredDeprecated The request.Request parameter for Glean.Client.Insights.Retrieve() was changed. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking change to the request.Request parameter in Insights.Retrieve() - Review usage of Insights.Retrieve() for compatibility with this update. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking change to the request.Request parameter in Insights.Retrieve() - Review usage of Insights.Retrieve() for compatibility with this update. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.9.2) September 26 2025 ## REST API: changes (endpoints) 2025-09-26 APINoteworthy 1 endpoint added. **Read more** ## Changes * Added endpoint: /debug/{datasource}/document/events. * 1 endpoint added. September 25 2025 ## api-client-typescript v0.10.3 API ClientsBreaking Added the channelType field to response.results.\[].primaryEntry.digest.sections in glean.client.search.retrieveFeed(). **Read more** ## Changes * New response field: channelType - No breaking changes - Applies to retrieveFeed() method in the search client. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.10.3) ## api-client-python v0.8.1 API Clients The response object for glean.client.search.retrieve\_feed() now includes the channel\_type field in response.results.\[].primary\_entry.digest.sections. **Read more** ## Changes * Added channel\_type to response.results.\[].primary\_entry.digest.sections in retrieve\_feed(). ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.8.1) ## api-client-java v0.9.1 API Clients The response object for glean.client.search.retrieveFeed() now includes the channelType field in response.results.\[].primaryEntry.digest.sections. **Read more** ## Changes * Added response.results.\[].primaryEntry.digest.sections.\[].channelType to the API response. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.9.1) ## api-client-go v0.9.1 API Clients Added the ChannelType field to response.Results.\[].PrimaryEntry.Digest.Sections in Glean.Client.Search.RetrieveFeed(). **Read more** ## Changes * New ChannelType field is now included in the RetrieveFeed response structure. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.9.1) September 24 2025 ## api-client-typescript v0.10.1 API ClientsBreakingAction requiredDeprecated Breaking changes to multiple request and response fields across document, answer, shortcut, chat, search, announcement, collection, and verification APIs; new parameter added to chat file retrieval. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking changes to multiple request and response fields across document, answer, shortcut, chat, search, announcement, collection, and verification APIs; new parameter added to chat file retrieval. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking changes to multiple request and response fields across document, answer, shortcut, chat, search, announcement, collection, and verification APIs; new parameter added to chat file retrieval. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.10.1) ## api-client-python v0.8.0 API ClientsBreakingAction requiredDeprecated Breaking changes to multiple API endpoints, including request and response field modifications, and a new parameter added to chat file retrieval. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking changes to multiple API endpoints, including request and response field modifications, and a new parameter added to chat file retrieval. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking changes to multiple API endpoints, including request and response field modifications, and a new parameter added to chat file retrieval. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.8.0) ## api-client-java v0.9.0 API ClientsBreakingAction requiredDeprecated Breaking changes to request and response fields across multiple endpoints, including addedRoles, relatedDocuments, and messages; new chatId parameter added to chat file retrieval. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking changes to request and response fields across multiple endpoints, including addedRoles, relatedDocuments, and messages; new chatId parameter added to chat file retrieval. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking changes to request and response fields across multiple endpoints, including addedRoles, relatedDocuments, and messages; new chatId parameter added to chat file retrieval. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.9.0) ## api-client-go v0.9.0 API ClientsBreakingAction requiredDeprecated Breaking changes to request and response fields across multiple endpoints, including changes to DocumentSpecs, AddedRoles, RelatedDocuments, and Messages; new parameter added to Chat.RetrieveFiles. **Read more** ## Action Required * Review this breaking change before upgrading: Breaking changes to request and response fields across multiple endpoints, including changes to DocumentSpecs, AddedRoles, RelatedDocuments, and Messages; new parameter added to Chat.RetrieveFiles. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * Breaking changes to request and response fields across multiple endpoints, including changes to DocumentSpecs, AddedRoles, RelatedDocuments, and Messages; new parameter added to Chat.RetrieveFiles. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.9.0) September 18 2025 ## mcp-config-schema v0.13.1 MCP Fixed an issue where ChatGPT/Claude Enterprise incorrectly required mcp-remote. **Read more** ## Changes * Upgraded to ESLint 9 with flat config. * Internal changes only. * Committers: David J. Hamilton, Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.13.1) September 12 2025 ## mcp-config-schema v0.13.0 MCPNoteworthy Added support for white labeling product names. **Read more** ## Changes * Enables customization of product names for branding. * Enhancement improves flexibility for partners and clients. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.13.0) ## configure-mcp-server v1.0.0 MCP Initial MCP server configurator CLI for Glean released, enabling core configuration flows for MCP Server integration. **Read more** ## Changes * Implements core configuration flows for MCP Server integration. * Enhancement release. * Committers: Chris Freeman, David Hamilton, Nathaniel Furniss, Steve Calvert, Robert Jackson. ## Source * [Release notes](https://github.com/gleanwork/configure-mcp-server/releases/tag/v1.0.0) September 10 2025 ## mcp-config-schema v0.12.2 MCP The one click URL generator no longer relies on Buffer in browser environments. **Read more** ## Changes * Bug fix: Updated URL generator dependency for browser compatibility - Committer: Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.12.2) September 2 2025 ## mcp-config-schema v0.12.1 MCP Exports for server name utilities are now included in the browser bundle. **Read more** ## Changes * Fixed missing exports of server name utilities in browser builds - Ensures server name utilities are accessible in browser environments - Committer: Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.12.1) ## mcp-config-schema v0.12.0 MCP Adds centralized CLI configuration command building and protocol-level improvements. **Read more** ## Changes * Adds centralized CLI configuration command building and protocol-level improvements. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.12.0) August 29 2025 ## mcp-config-schema v0.11.0 MCP Expanded configuration support coverage for MCP protocol. **Read more** ## Changes * Enhanced config schema to support a wider range of use cases - Addresses protocol-level improvements for broader compatibility. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.11.0) ## mcp-config-schema v0.10.0 MCP Refactored to use a builder pattern and standardized config generation, with added one-click support for VS Code. **Read more** ## Changes * Refactored internal logic to implement a builder pattern - Standardized configuration generation process. * Added one-click support for VS Code as per official documentation. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.10.0) ## api-client-python v0.7.3 API Clients Added group.datasource\_instance fields to multiple request and response objects across announcements, answers, chat, collections, documents, entities, pins, search, shortcuts, and verification APIs. **Read more** ## Changes * Added group.datasource\_instance fields to multiple request and response objects across announcements, answers, chat, collections, documents, entities, pins, search, shortcuts, and verification APIs. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.7.3) August 26 2025 ## mcp-server v0.8.0 MCP Device code flow support has been removed and the configure command has been streamlined, with updates to the MCP config schema. **Read more** ## Changes * Device code flow support has been removed and the configure command has been streamlined, with updates to the MCP config schema. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.8.0) ## mcp-config-schema v0.9.0 MCP Exported CLIENT and CLIENT\_DISPLAY\_NAME constants are now available in the browser bundle. **Read more** ## Changes * Enhancement to protocol-level exports - Improves configuration flexibility for browser integrations. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.9.0) ## mcp-config-schema v0.8.0 MCPNoteworthy Goose now supports native HTTP protocol and CLIENTS.md has been updated to reflect recent configuration changes. **Read more** ## Changes * Enhanced Goose with native HTTP support. * Updated CLIENTS.md after config changes - Changes contributed by Chris Freeman and Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.8.0) August 23 2025 ## mcp-config-schema v0.7.0 MCP Adds support for Claude desktop on Linux platforms. **Read more** ## Changes * Enhancement: Claude desktop is now available for Linux users - Committer: Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.7.0) ## mcp-config-schema v0.6.0 MCP Adds the ability to build glean MCP server names. **Read more** ## Changes * Enhancement to server naming functionality - No protocol-level or config schema changes noted - Committer: Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.6.0) August 21 2025 ## mcp-config-schema v0.4.4 MCP Claude integration updated to use Teams/Enterprise for organization connectors. **Read more** ## Changes * Bug fix addressing connector configuration for Claude - No protocol-level or config schema changes noted - Committer: Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.4.4) ## mcp-config-schema v0.4.3 MCP Fixed an issue with broken package exports affecting protocol-level integration. **Read more** ## Changes * Package exports now function correctly - Ensures compatibility with protocol consumers - No config schema changes required. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.4.3) ## mcp-config-schema v0.4.2 MCP Updated cursor to use HTTP native protocol and made internal improvements. **Read more** ## Changes * Fixed bug by updating cursor to HTTP native. * Added CODEOWNERS file. * Updated generate-clients script. * Added CLAUDE.md documentation. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.4.2) ## mcp-config-schema v0.4.0 MCP Partial output of configuration files is now supported. **Read more** ## Changes * Enhancement: Adds the ability to output partial configs - Committer: Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.4.0) August 19 2025 ## mcp-config-schema v0.2.0 MCP Mcp-config-schema v0.2.0. **Read more** ## Changes * Mcp-config-schema v0.2.0. ## Source * [Release notes](https://github.com/gleanwork/mcp-config-schema/releases/tag/v0.2.0) August 12 2025 ## api-client-typescript v0.9.1 API Clients Updated TypeScript API client to v0.9.1, based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.551.0 (2.610.0). **Read more** ## Changes * Includes all changes from OpenAPI Doc 0.9.0 - Generated new TypeScript SDK version 0.9.1 - Published as NPM package v0.9.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.9.1) ## api-client-python v0.7.2 API Clients Updated Python API client to version 0.7.2 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.603.0. **Read more** ## Changes * Python client generated at v0.7.2 - Released to PyPI as v0.7.2. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.7.2) ## api-client-java v0.8.0 API Clients Released java v0.8.0 API client based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.603.0 (2.681.1). **Read more** ## Changes * Published to Maven Central as v0.8.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.8.0) ## api-client-go v0.8.0 API Clients Updated Go API client to v0.8.0 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.603.0 (2.681.1). **Read more** ## Changes * Generated Go client version v0.8.0 - Based on OpenAPI Doc 0.9.0 - Uses Speakeasy CLI 1.603.0 (2.681.1). ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.8.0) August 8 2025 ## mcp-server v0.7.18 MCPNoteworthy Resolved an issue affecting the publishing process. **Read more** ## Changes * Addressed a problem that prevented successful publishing - No protocol-level or config schema changes introduced. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.18) ## mcp-server v0.7.17 MCP Support for OneLogin device flow added and improved error handling and platform compatibility in configure-mcp-server and mcp-server-utils. **Read more** ## Changes * Added OneLogin device flow support. * Improved platform support for Claude Code. * Added error when refresh token not issued. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.17) July 24 2025 ## glean-indexing-sdk v0.2.0 Glean Indexing SDK Adds support for forced restarts of indexing uploads and introduces a property definition builder. **Read more** ## Changes * Forced restarts can now be triggered for indexing uploads - New property definition builder helper added. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v0.2.0) July 23 2025 ## glean-indexing-sdk v0.1.0 Glean Indexing SDK Introduces a property definition builder and fixes the format of tags for release. **Read more** ## Changes * Adds property definition builder - Fixes tag formatting for release - Adds additional model for re-export. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v0.1.0) July 22 2025 ## mcp-server v0.7.16 MCP Support for Azure added and CVE-2025-7783 addressed in MCP server configuration and utilities. **Read more** ## Changes * Bug fix: Support for Azure in configure-mcp-server and mcp-server-utils - Security fix: CVE-2025-7783 resolved in configure-mcp-server and local-mcp-server - Contributor: David J. Hamilton. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.16) ## mcp-server v0.7.15 MCP Bug fixes and dependency updates for MCP server utilities, including security improvements and version pinning. **Read more** ## Changes * Updated mcp-remote fork to version 0.2.2 to address CVE-2025-6514 - Pinned connect-mcp-server dependency in mcp-server-utils - Applied fixes to configure-mcp-server and mcp-server-utils. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.15) July 21 2025 ## glean-agent-toolkit 0.3.0 Glean Agent Toolkit Enhanced parameter schemas now support Field metadata, and developer setup is improved with formatting and type error fixes. **Read more** ## Changes * Added enhanced parameter schemas with Field metadata support. * Added newline to .cz.toml. * Fixed type errors during task:lint. ## Source * [Release notes](https://github.com/gleanwork/glean-agent-toolkit/releases/tag/0.3.0) July 18 2025 ## langchain-glean v0.3.3 langchain-glean The propagation of act\_as and GLEAN\_ACT\_AS has been ensured, and tests have been updated to use the new SDK import path. **Read more** ## Changes * Ensure act\_as and GLEAN\_ACT\_AS are propagated - Update tests with new SDK import path. ## Source * [Release notes](https://github.com/gleanwork/langchain-glean/releases/tag/v0.3.3) July 15 2025 ## mcp-server v0.7.14 MCP Fixed an issue with Azure OAuth support. **Read more** ## Changes * Resolved a bug affecting Azure OAuth authentication - Change contributed by David J. Hamilton. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.14) July 11 2025 ## mcp-server v0.7.13 MCP Users can now manually open the device flow verification page in mcp-server-utils. **Read more** ## Changes * Enhancement to device flow verification process - Improves user control during authentication - Contributor: David J. Hamilton. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.13) ## api-client-typescript v0.8.1 API ClientsBreaking Updated TypeScript API client to version 0.8.1 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Includes changes generated from OpenAPI Doc 0.9.0 - Released as NPM package version 0.8.1 - No additional details or breaking changes specified in the release notes. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.8.1) ## api-client-python v0.7.1 API Clients Python API client updated to version 0.7.1, generated from OpenAPI Doc 0.9.0 using Speakeasy CLI 1.580.2 (2.656.5). **Read more** ## Changes * Based on OpenAPI Doc 0.9.0 - Python client generated at v0.7.1 - Released on PyPI as v0.7.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.7.1) ## api-client-java v0.7.0 API Clients Released java API client v0.7.0 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.580.2. **Read more** ## Changes * Published to Maven Central as v0.7.0 - Generated from OpenAPI Doc 0.9.0 - Built using Speakeasy CLI 1.580.2 (2.656.5). ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.7.0) ## api-client-go v0.7.1 API Clients Updated Go API client to version 0.7.1 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.580.2 (2.656.5). **Read more** ## Changes * Reflects changes from OpenAPI Doc 0.9.0 - Generated using Speakeasy CLI 1.580.2 (2.656.5) - Go API client now at version 0.7.1. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.7.1) July 8 2025 ## mcp-server v0.7.12 MCP Improved document reading with related docs and updated documentation by removing support email. **Read more** ## Changes * Fixed reading of documents with related docs in local-mcp-server. * Removed support email from documentation in local-mcp-server. * Committers: David J. Hamilton, Steve Calvert. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.12) July 7 2025 ## api-client-typescript v0.7.1 API Clients Updated TypeScript API client to version 0.7.1 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.551.0 (2.610.0). **Read more** ## Changes * Includes latest changes from OpenAPI specification 0.9.0 - - Released as @gleanwork/api-client v0.7.1 on NPM. ## Source * [Release notes](https://github.com/gleanwork/api-client-typescript/releases/tag/v0.7.1) ## api-client-python v0.7.0 API Clients Updated Python API client to version 0.7.0 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.576.1 (2.651.2). **Read more** ## Changes * Python SDK generated at v0.7.0 - Released to PyPI as v0.7.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-python/releases/tag/v0.7.0) ## api-client-java v0.6.0 API Clients Released java API client v0.6.0 based on OpenAPI Doc 0.9.0 and Speakeasy CLI 1.576.1 (2.651.2). **Read more** ## Changes * Published to Maven Central as v0.6.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-java/releases/tag/v0.6.0) ## api-client-go v0.7.0 API Clients Updated Go API client to v0.7.0 based on OpenAPI Doc 0.9.0. **Read more** ## Changes * Generated using Speakeasy CLI 1.576.1 (2.651.2) - Reflects changes from OpenAPI Doc 0.9.0 - Go client version incremented to v0.7.0. ## Source * [Release notes](https://github.com/gleanwork/api-client-go/releases/tag/v0.7.0) June 26 2025 ## langchain-glean v0.3.2 langchain-glean Tests updated for new SDK import path and api-client upgraded to 0.4.2. **Read more** ## Changes * Updated tests to use the new SDK import path - Upgraded api-client dependency to version 0.4.2. ## Source * [Release notes](https://github.com/gleanwork/langchain-glean/releases/tag/v0.3.2) June 25 2025 ## mcp-server v0.7.11 MCPNoteworthy Added support for Claude Code and refactored goose client configuration in configure-mcp-server. **Read more** ## Changes * Enhancement: Added Claude Code support. * Affected component: configure-mcp-server. * Internal: Refactored goose client config. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.11) June 20 2025 ## mcp-server v0.7.10 MCP Fixed an issue with the remote token header in the configure-mcp-server component. **Read more** ## Changes * Bug fix in configure-mcp-server - Protocol-level header handling corrected - Committer: David J. Hamilton. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.10) ## glean-indexing-sdk v0.0.2 Glean Indexing SDK Fixed tag formatting for release and added an additional model for re-export. **Read more** ## Changes * Fixed format of tags in release. * Added additional model for re-export. ## Source * [Release notes](https://github.com/gleanwork/glean-indexing-sdk/releases/tag/v0.0.2) June 19 2025 ## mcp-server v0.7.9 MCPNoteworthy Added support for Goose as a client and improved internal tooling and formatting for MCP server components. **Read more** ## Changes * Support for Goose as a client in configure-mcp-server - Prettier configuration fix and format on merge enabled - Ensured releaser runs pnpm login. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.9) ## mcp-server v0.7.8 MCP Added version-based warning functionality and improved documentation and build processes. **Read more** ## Changes * Added version-based warning functionality and improved documentation and build processes. ## Source * [Release notes](https://github.com/gleanwork/mcp-server/releases/tag/v0.7.8) June 17 2025 ## OAuth for Remote MCP Server (Private Beta) MCP You can now use the Device Authorization Flow with the same SSO provider configured in Glean to authenticate users connecting their clients to Glean’s remote MCP server. **Read more** ## Changes * You can now use the Device Authorization Flow with the same SSO provider configured in Glean to authenticate users connecting their clients to Glean’s remote MCP server. * See here for setup instructions. * Note: OAuth is also enabled for the local MCP server. June 6 2025 ## Go API Client v0.6.0 SDKBreakingAction requiredDeprecated **Breaking Changes:** Updated minimum go version to 1.22 (from 1.20). **Read more** ## Action Required * Verify your Go toolchain uses 1.22 or later. ## Changes * This release only includes breaking or deprecated changes. ## Breaking Changes * **Breaking Changes:** Updated minimum go version to 1.22 (from 1.20). May 31 2025 ## Updates to API Reference Code Snippets Documentation Update the generated code snippets that are displayed throughout the Client API Reference and Indexing API Reference docs to include the required `instance` parameter in the API client constructor. **Read more** ## Changes * Update the generated code snippets that are displayed throughout the Client API Reference and Indexing API Reference docs to include the required `instance` parameter in the API client constructor. ## Python API Client v0.6.0 - Breaking Changes SDKAction required The Python API client now uses a namespaced package structure. All imports must be updated from `glean` to `glean.api_client`. **Read more** ## Action Required * Update imports to use the new paths. ## Changes * Import paths have changed from `from glean import ...` to `from glean.api_client import ...`. * This affects all classes including `Glean`, `models`, and other API components. * `from glean import` → `from glean.api_client import`. * `from glean.` (but not glean.api\_client) → `from glean.api_client.`. * This change affects all Python API client users. * No functional changes to the API itself - only import paths. * Ensure you're using the latest version of the Python API client package. May 27 2025 ## Run agent API bug fix in client libraries API We fixed a bug that caused Run Agent Wait for Output (`/agents/run/wait`) to return empty responses when used with the API Client libraries. **Read more** ## Changes * We fixed a bug that caused Run Agent Wait for Output (`/agents/run/wait`) to return empty responses when used with the API Client libraries. * We also fixed a bug that produced non-SSE compliant output when using the Run Agent Stream Output (`/agents/run/stream`) endpoint. ## Remote MCP Server (private beta) MCPNoteworthy With Glean’s remote MCP server, you can access Glean from MCP clients like Claude Desktop, Cursor, or Goose. **Read more** ## Changes * With Glean’s remote MCP server, you can access Glean from MCP clients like Claude Desktop, Cursor, or Goose. Your Glean admin can enable a remote MCP server to expose Glean agents and tools. To join the MCP Server private beta, contact your Glean account team. May 23 2025 ## New Features for Tools and Agents API, API Clients, MCP APIBreakingAction requiredNoteworthy Tools and agents APIs added list and call endpoints, API clients and MCP added support, and search request-body handling was corrected. **Read more** ## Action Required * Update request body handling before upgrading. ## Changes * Added `GET /tools/list` and `POST /tools/call` to the Client REST API. * Added API client support for `GET /tools/list` and `POST /tools/call`. * Added MCP server configuration support for VS Code. * Fixed the OpenAPI request body schema for `/rest/api/v1/search`, `/rest/api/v1/recommendations`, and `/rest/api/v1/adminsearch`. ## Breaking Changes * Python API client search method parameters changed to align with other API methods because the OpenAPI request body is now marked as required. May 16 2025 ## New Governance and Agent API Features API Governance Admin API surface (10 endpoints). **Read more** ## Changes * Governance Admin API surface (10 endpoints). * Policies: retrieve, update, list, create, download - Reports:. * Visibility Overrides: listVisibilityOverrides,. * Agent API brought up to the LangChain Agent-Protocol (Agents & Runs stages). * Retrieve an Agent `GET /agents/ {agent_id}`. * Retrieve an Agent's Schemas `GET /agents/{agent_id}/schemas`. * List Agents `POST /agents/search`. * Run an Agent `POST /agents/runs/wait`. * Run an Agent with streaming `POST /agents/runs/stream`. * Python API client: resolved "unclosed async coroutine" warning in async transport. * Replaced legacy alpha Run-Workflow endpoints with the standard Agent-Protocol equivalents (see above). * Governance endpoints introduce new permission scopes (`governance.read`,. * Python 0.4.1 uploaded to PyPI, requires 3.8+. * TypeScript 0.4.1 published, ESM, bundled types. * Go module path `github.com/gleaninc/glean-sdk-go/v4.1.0`. * Java 0.4.1 available on Maven Central (`com.glean:glean-sdk:0.4.1`). May 10 2025 ## Official API Clients Release SDK Released official API clients for Glean in multiple languages, replacing. **Read more** ## Changes * Released official API clients for Glean in multiple languages, replacing. * New API clients are now. * Python. * TypeScript. * Go. * Java. * Updated documentation with new installation instructions and usage examples. * Visit the API Clients page for more information. May 2 2025 ## New Indexing API Endpoints for Documents and Troubleshooting API /updatepermissions Endpoint to update document permissions: Generally available. **Read more** ## Changes * /updatepermissions Endpoint to update document permissions: Generally available. * /debug/datasource/documents Troubleshooting endpoint for batch queries: Generally available. April 24 2025 ## Deprecate Previous Way of Processing Response Message Stream APIDeprecated In the /chat API, the previous way of processing the response message stream has been deprecated as a result of the launch of LLM-generated citations. **Read more** ## Changes * LLM citations are interleaved within the response text fragments. Each fragment can have "text" or "citation". * If streaming is set to False, the response may still be broken up into across multiple fragments. * The citations interleaved inside the response do not have reference ranges (start and end indices) or snippets as they used. * For backwards compatibility, we still return a chat message with citations at the end in the old format, except there will be no startIndex and no snippets. April 18 2025 ## Deprecate x-scio-actas Header in Favor of x-glean-actas APIAction requiredDeprecated Deprecate X-Scio-ActAs in favor of X-Glean-AtAs. X-Scio-ActAs continues. **Read more** ## Action Required * Plan migration away from deprecated behavior. ## Changes * Deprecate X-Scio-ActAs in favor of X-Glean-AtAs. X-Scio-ActAs continues. April 17 2025 ## Debug Endpoints Permission Status API `/debug/{datasource}/document` - New response field `permissionIdentityStatus` under `status`: Provides information regarding upload status of users and groups specified in document permissions. **Read more** ## Changes * `/debug/{datasource}/document` - New response field `permissionIdentityStatus` under `status`: Provides information regarding upload status of users and groups specified in document permissions. * `/debug/{datasource}/documents` - New response field `permissionIdentityStatus` under `status`: Provides information regarding upload status of users and groups specified in document permissions. March 26 2025 ## Update Developer Site Website The developer site has been updated with a new look, content, and features. Some new features include:. **Read more** ## Changes * This documentation site is now open sourced on GitHub. * This changelog page, which gives you a single place to see updates across the platform. * Ability to switch between light and dark mode. * A new API Clients page with documentation for all available API Clients. * A list of community projects and resources to help you get started with Glean. * Each API has its own documentation page with detailed information. March 11 2025 ## Add MCP MCP Added a Model Context Protocol (MCP) server implementation for Glean's search and chat capabilities on GitHub. **Read more** ## Changes * Added a Model Context Protocol (MCP) server implementation for Glean's search and chat capabilities on GitHub. * This server provides a standardized interface for AI models to interact with Glean's content search and conversational AI features through stdio communication. * Visit the Agents for more information. March 5 2025 ## LangChain SDK Release SDK Added LangChain SDK for Python on GitHub. This SDK provides a simple interface for interacting with Glean's search and chat capabilities when using LangChain. **Read more** ## Changes * Added LangChain SDK for Python on GitHub. This SDK provides a simple interface for interacting with Glean's search and chat capabilities when using LangChain. * Visit the Agents for more information. February 19 2025 ## Update Permissions Endpoint APINoteworthy Beta launch of new endpoint to update document permissions -. **Read more** ## Changes * Beta launch of new endpoint to update document permissions -. * Beta launch of new troubleshooting endpoint for batch queries. --- Cookbooks # Recipes for building on Glean Runnable patterns that go from problem to a working demo to scaffolded starter code — with the architecture, auth, and permissions laid out for each. Start here · Chat ## Stream a cited Chat response Use the modern Platform Chat API to send a permission-aware question, continue the conversation, and read server-sent events with the official TypeScript API client's `createStream` `EventStream`. \~20 minChatPlatform API [Open recipe](/cookbook/streaming-chat-with-citations.md) What you’ll build 1 Your CLI prompt and follow-up 2 TypeScript API client createStream 3 Platform Chat permission-aware response One starter recipe per collection Pause CapabilityAll capabilities (all) SurfaceAll surfaces (all) 14 recipe s ## Chat 1 recipe · Beginner Ask Glean a question from code and stream the answer with its citations. [1](/cookbook/streaming-chat-with-citations.md) [ of ](/cookbook/streaming-chat-with-citations.md) [1](/cookbook/streaming-chat-with-citations.md) [Stream a cited Chat response](/cookbook/streaming-chat-with-citations.md) [Use the modern Platform Chat API to send a permission-aware question, continue the conversation, and read server-sent events with the official TypeScript API client's `createStream` `EventStream`.](/cookbook/streaming-chat-with-citations.md) [\~20 min·BeginnerPlatform API](/cookbook/streaming-chat-with-citations.md) ## Search 1 recipe · Beginner Search your Glean content using the filters each datasource supports. [1](/cookbook/search-with-discovered-filters.md) [ of ](/cookbook/search-with-discovered-filters.md) [1](/cookbook/search-with-discovered-filters.md) [Search Glean with discovered filters](/cookbook/search-with-discovered-filters.md) [Use the official TypeScript API client to search across all of your Glean content by default, or discover datasources and common filter fields before applying an explicit selection to permission-aware Platform Search.](/cookbook/search-with-discovered-filters.md) [\~15 min·BeginnerPlatform API](/cookbook/search-with-discovered-filters.md) ## Agents 3 recipe s · Beginner → Advanced Run a published agent from code, approve its actions before they happen, then build an on-call dashboard that puts a person in front of every action. [1](/cookbook/triage-support-issue.md) [ of ](/cookbook/triage-support-issue.md) [3](/cookbook/triage-support-issue.md) [Triage one support issue with a Glean Agent](/cookbook/triage-support-issue.md) [Give a published support-triage Agent one issue URL or description and get an evidence-backed summary, next diagnostic step, and customer-safe response.](/cookbook/triage-support-issue.md) [\~15 min·BeginnerPlatform API](/cookbook/triage-support-issue.md) [2](/cookbook/human-in-the-loop-agent.md) [ of ](/cookbook/human-in-the-loop-agent.md) [3](/cookbook/human-in-the-loop-agent.md) [Approve an agent action from a CLI](/cookbook/human-in-the-loop-agent.md) [Start a durable agent run from TypeScript, see the exact tool call it wants to make, and approve or reject it before it happens. The agent only sends you a Slack DM.](/cookbook/human-in-the-loop-agent.md) [\~5 min·BeginnerPlatform APIAgents](/cookbook/human-in-the-loop-agent.md) [3](/cookbook/oncall-copilot.md) [ of ](/cookbook/oncall-copilot.md) [3](/cookbook/oncall-copilot.md) [On-call Copilot](/cookbook/oncall-copilot.md) [An on-call dashboard that takes an alarm to a proposed action and puts a person in front of that action. It names a cause only when a past incident backs it, turns away an approver who is neither on call nor the service owner, and escalates instead of approving itself when nobody answers. It runs on recorded incidents, so you can watch it refuse things before connecting it to anything.](/cookbook/oncall-copilot.md) [\~1.5 hr·AdvancedPlatform APIAgents](/cookbook/oncall-copilot.md) ## Skills 3 recipe s · Beginner → Intermediate Publish and check one skill, import skills from GitHub, then manage versioned skill bundles. [1](/cookbook/validate-and-publish-skill.md) [ of ](/cookbook/validate-and-publish-skill.md) [3](/cookbook/validate-and-publish-skill.md) [Validate skill publishing](/cookbook/validate-and-publish-skill.md) [Validate a local SKILL.md, test publishing and retrieval in Glean, confirm the downloaded file matches, and delete the test skill.](/cookbook/validate-and-publish-skill.md) [\~15 min·BeginnerPlatform API](/cookbook/validate-and-publish-skill.md) [2](/cookbook/import-skill-from-github.md) [ of ](/cookbook/import-skill-from-github.md) [3](/cookbook/import-skill-from-github.md) [Import a skill from GitHub](/cookbook/import-skill-from-github.md) [Preview a public GitHub skill on a branch URL with the official TypeScript API client, import the selected URL, sync that captured skill, confirm it with get and list, then delete only IDs this run created.](/cookbook/import-skill-from-github.md) [\~20 min·IntermediatePlatform API](/cookbook/import-skill-from-github.md) [3](/cookbook/skill-publishing-pipeline.md) [ of ](/cookbook/skill-publishing-pipeline.md) [3](/cookbook/skill-publishing-pipeline.md) [Publish a versioned skill bundle](/cookbook/skill-publishing-pipeline.md) [After a first persist, prove name-based version supersession, retrieve a specific version, stage downloaded bytes in a bounded zip sandbox, and delete only the run-owned skill.](/cookbook/skill-publishing-pipeline.md) [\~20 min·IntermediatePlatform API](/cookbook/skill-publishing-pipeline.md) ## Triggers 1 recipe · Beginner Register a calendar Trigger and receive its signed events. [![](/img/cookbook/glean-trigger.svg)1](/cookbook/customer-meeting-prep-trigger.md) [ of ](/cookbook/customer-meeting-prep-trigger.md) [1](/cookbook/customer-meeting-prep-trigger.md) [Trigger a customer meeting prep request](/cookbook/customer-meeting-prep-trigger.md) [Register a calendar Trigger through the Platform Triggers API and deliver one signed event to a small receiver that prints a customer-meeting prep request.](/cookbook/customer-meeting-prep-trigger.md) [\~20 min·BeginnerPlatform API](/cookbook/customer-meeting-prep-trigger.md) ## Complete apps 5 recipe s · Beginner → Intermediate End-to-end builds that combine features, from no-code builders to full apps you can run. [![](/img/cookbook/lovable.svg)1](/cookbook/no-code-it-helpdesk-lovable.md) [ of ](/cookbook/no-code-it-helpdesk-lovable.md) [5](/cookbook/no-code-it-helpdesk-lovable.md) [Build an IT helpdesk page in Lovable](/cookbook/no-code-it-helpdesk-lovable.md) [Paste a prompt into Lovable to get a private page that answers common IT questions from your Glean docs.](/cookbook/no-code-it-helpdesk-lovable.md) [\~45 min·BeginnerClient API](/cookbook/no-code-it-helpdesk-lovable.md) [2](/cookbook/no-code-pto-lookup-replit.md) [ of ](/cookbook/no-code-pto-lookup-replit.md) [5](/cookbook/no-code-pto-lookup-replit.md) [Build a PTO lookup page in Replit](/cookbook/no-code-pto-lookup-replit.md) [Paste a prompt into Replit Agent to get a private page that answers PTO and benefits questions from your Glean docs.](/cookbook/no-code-pto-lookup-replit.md) [\~45 min·BeginnerClient API](/cookbook/no-code-pto-lookup-replit.md) [3](/cookbook/onboarding-hub.md) [ of ](/cookbook/onboarding-hub.md) [5](/cookbook/onboarding-hub.md) [Onboarding Hub: a day-one checklist grounded in your own docs](/cookbook/onboarding-hub.md) [A guided first-week hub for new hires: a checklist with progress, and every step able to answer itself from your own onboarding content.](/cookbook/onboarding-hub.md) [\~45 min·IntermediateWeb SDKPlatform API](/cookbook/onboarding-hub.md) [4](/cookbook/rfp-responder.md) [ of ](/cookbook/rfp-responder.md) [5](/cookbook/rfp-responder.md) [Answer an RFP or security questionnaire](/cookbook/rfp-responder.md) [A review app that drafts cited answers to an RFP or security questionnaire, leaves unsupported questions blank, and requires a person to approve each answer. It starts with recorded Chat responses, so you can inspect its refusal paths before connecting it to Glean.](/cookbook/rfp-responder.md) [\~45 min·IntermediatePlatform API](/cookbook/rfp-responder.md) [5](/cookbook/customer-360.md) [ of ](/cookbook/customer-360.md) [5](/cookbook/customer-360.md) [Customer 360: an account page built from your own content](/cookbook/customer-360.md) [One page per account — status, risks, and a drill-in chat — assembled from whatever your instance already knows about that customer. No CRM export, no separate index.](/cookbook/customer-360.md) [\~1 hr·IntermediatePlatform API](/cookbook/customer-360.md) --- [Complete apps](/cookbook.md#complete-apps) [ · ](/cookbook.md#complete-apps) [5](/cookbook.md#complete-apps) [ of](/cookbook.md#complete-apps) [ ](/cookbook.md#complete-apps) [5](/cookbook.md#complete-apps) # Customer 360: an account page built from your own content One page per account — status, risks, and a drill-in chat — assembled from whatever your instance already knows about that customer. No CRM export, no separate index. \~1 hrIntermediate ![Customer 360 workspace with account evidence and an assistant conversation](/img/cookbook/previews/customer-360/preview.webp)View preview Problem Account executives jump between CRM notes, renewal docs, and security packets to prep a single customer conversation. This puts those sources in one account workspace with evidence coverage, compact source sections, and an above-the-fold assistant thread — so the next call needs one tab, not nine. Everything displayed comes from what your instance already knows about that account. The overview shows only the account name you set and counts taken straight from the Search results; assistant claims remain cited rather than turning loose search matches into unsupported CRM facts. This recipe builds the page two ways: Platform Search plus Platform Chat for an open-ended dashboard, or Agents `createRun` for a prescriptive QBR-ready brief. Architecture Account pageEvidence overview + sources + assistant thread Path A: Search + Chatglean.search.query + glean.chat.create Path B: Agentsglean.agents.createRun Glean Platform APIpermission-aware retrieval + synthesis Prerequisites Node 20 or newer Your work email, so the sign-in command can find your Glean tenant. If your tenant cannot use OAuth, you need a Glean API token instead, scoped to the path you pick The name of one of your own customers, spelled the way it appears in your Glean documents. You put that name in GLEAN\_ACCOUNT\_NAME, and the page is built around it. This is the customer you are reading about, not your own company or your Glean instance Path B only: a conversational Account Brief agent in Agent Builder, whose ID you put in GLEAN\_AGENT\_ID. A form-triggered agent will not work Steps 1 **Pick a path** Both paths build the same page from the same content, and each has its own set of steps. Choose Platform Search Chat if you want the page to assemble the account itself, which is the better starting point. Choose Platform Agents if you already run an Account Brief agent and want the page to show what that agent says. * Platform Search Chat * Platform Agents 1 **Copy the project onto your machine** Creates a customer-360 folder in whatever directory you run this from. Every command after this one runs inside that folder. ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/customer-360/platform-search-chat customer-360 ``` 2 **Install dependencies** Installs the Glean API client and the small local server that serves the page. ``` cd customer-360 && npm install ``` 3 **See it work before you connect anything** Runs the whole page against recorded answers for a fictional account named Globex, so you can see what it produces before you connect anything. This needs no Glean credentials. ``` npm run verify:fixture ``` 4 **Sign in to Glean** Your email is used once to find which Glean tenant you belong to, then a browser window opens for you to approve access. The command creates the .env file for you and fills in GLEAN\_SERVER\_URL and GLEAN\_API\_TOKEN. If your tenant has not enabled OAuth, skip this command and do it by hand instead: copy .env.example to .env, then fill in your Glean instance URL and a Glean API token that has the SEARCH and CHAT scopes. ``` npm run login -- --email "" ``` 5 **Choose which customer the page is about** Signing in does not pick an account for you, so open .env and set GLEAN\_ACCOUNT\_NAME to one of your own customers, for example Acme Logistics. Spell it the way your Glean documents spell it, because the page searches your content for that exact name and builds the whole page from what it finds. 6 **Check it against your own content** Takes 1 to 3 minutes. It starts its own server, asks your Glean instance the three demo questions about the account you chose, and fails if any answer comes back without citing a real document. ``` npm run verify ``` 7 **Open the page** Starts the server and prints a Local URL. Open that URL in your browser. ``` npm start ``` 1 **Build the Account Brief agent** In Agent Builder, create a conversational agent that writes an account brief, and copy its ID from the browser URL. It must be conversational, because this page sends it a question and reads the reply. A form-triggered agent will not work here. 2 **Copy the project onto your machine** Creates a customer-360 folder in whatever directory you run this from. Every command after this one runs inside that folder. ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/customer-360/platform-agents customer-360 ``` 3 **Install dependencies** Installs the Glean API client and the small local server that serves the page. ``` cd customer-360 && npm install ``` 4 **Sign in to Glean** Your email is used once to find which Glean tenant you belong to, then a browser window opens for you to approve access. The command creates the .env file for you and fills in GLEAN\_SERVER\_URL and GLEAN\_API\_TOKEN. If your tenant has not enabled OAuth, skip this command and do it by hand instead: copy .env.example to .env, then fill in your Glean instance URL and a Glean API token that has the SEARCH and AGENTS scopes. ``` npm run login -- --email "" ``` 5 **Choose the customer and point at your agent** Signing in does not pick an account for you, so open .env and set two values. GLEAN\_ACCOUNT\_NAME is one of your own customers, for example Acme Logistics, spelled the way your Glean documents spell it. GLEAN\_AGENT\_ID is the ID of the Account Brief agent you created in Agent Builder. 6 **Check it against your own content** Takes 1 to 3 minutes. It starts its own server, confirms your agent answers for the account you chose, and fails if the brief comes back without citing a real document. ``` npm run verify ``` 7 **Open the page** Starts the server and prints a Local URL. Open that URL in your browser. ``` npm start ``` Take it further * Add a portfolio dashboard across a rep's whole book of business, with health and renewal countdown tiles per account. * Schedule a weekly headless job that re-runs the account's queries, diffs against the last run, and posts only what changed to Slack. * Wire push-to-CRM / push-to-Slack actions from the journey panel once you have a governed custom tool. Try it **What's the status of our renewal with that account?** Returns a non-empty answer citing real documents about the account you built the page around. Substitute the name when you ask — there is no fixed query text for a page built around whichever account you pick. **Give me a customer summary** Synthesizes across more than one source with a citation per claim, rather than restating a single document. **What are the renewal risks?** Either names risks grounded in cited content, or says it has none to report. It must not infer risk the sources don't support, and the account overview must show only the account name you set plus counts taken straight from the Search results. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:customer-360 ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/customer-360) Runs the recipe through the Glean cookbook plugin. [Complete apps](/cookbook.md#complete-apps) collection 1. 1[Build an IT helpdesk page in Lovable](/cookbook/no-code-it-helpdesk-lovable.md)Beginner 2. 2[Build a PTO lookup page in Replit](/cookbook/no-code-pto-lookup-replit.md)Beginner 3. 3[Onboarding Hub: a day-one checklist grounded in your own docs](/cookbook/onboarding-hub.md)Intermediate 4. 4[Answer an RFP or security questionnaire](/cookbook/rfp-responder.md)Intermediate 5. 5Customer 360: an account page built from your own contentIntermediate Auth **Platform Search Chat** Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (SEARCH, CHAT). **Platform Agents** Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (SEARCH, AGENTS). At a glance CapabilitiesSearch, Chat, Agents SurfacesPlatform API StatusShowcase Time\~1 hr Required scopes **Platform Search Chat** SEARCHCHAT **Platform Agents** SEARCHAGENTS --- [Triggers](/cookbook.md#triggers) [ · ](/cookbook.md#triggers) [1](/cookbook.md#triggers) [ of](/cookbook.md#triggers) [ ](/cookbook.md#triggers) [1](/cookbook.md#triggers) # Trigger a customer meeting prep request Register a calendar Trigger through the Platform Triggers API and deliver one signed event to a small receiver that prints a customer-meeting prep request. \~20 minBeginner Problem A customer meeting is about to start, but the team still has to reconstruct the account context manually. The first useful step is simply knowing that a matching meeting event arrived and turning it into a focused prep request. Code walkthrough The runnable service centralizes experimental Triggers API authentication and headers, discovers a tenant-supported preset, registers one signed webhook delivery, and verifies the event before printing a prep request. ### Call the experimental Triggers API safely Resolve the tenant-scoped token and attach the required experimental header to every request while rejecting invalid backend origins and failed responses. lib/glean-api.mjs ``` // One place that knows how to call the Glean Triggers API. // // The Triggers surface is experimental, so every request must carry // `x-glean-include-experimental: true` — without it the API answers 401 with // `Not allowed`, which reads like a credential problem and sends you looking in // the wrong place. Keeping the header here rather than in each script means a // new caller cannot forget it, and `verify.mjs` asserts it is still present. import { createGleanTokenProvider } from '@gleanwork/auth'; const EXPERIMENTAL_HEADER = 'x-glean-include-experimental'; export function apiBase(env = process.env) { const configured = (env.GLEAN_SERVER_URL || '').trim(); if (!configured) throw new Error('Set GLEAN_SERVER_URL in .env first.'); const server = new URL(configured); if ( server.protocol !== 'https:' || server.username || server.password || server.search || server.hash || (server.pathname && server.pathname !== '/') ) { throw new Error('Use a complete Glean backend HTTPS origin.'); } return `${server.origin}/api`; } export async function apiHeaders(env = process.env) { const token = await createGleanTokenProvider({ serverUrl: apiBase(env).replace(/\/api$/u, ''), scopes: ['triggers'], })(); return { authorization: `Bearer ${token}`, 'content-type': 'application/json', [EXPERIMENTAL_HEADER]: 'true', }; } export async function request(path, options = {}, env = process.env) { const response = await fetch(`${apiBase(env)}${path}`, { headers: await apiHeaders(env), ...options, }); const body = await response.json().catch(() => ({})); if (!response.ok) { // 401 on this surface usually means the OAuth grant was invalidated, not // that the token is malformed or the scope is missing — a token can carry // `triggers` and still be refused. Re-running the login command is the fix. const hint = response.status === 401 ? ' — if the token looks valid, sign in again: the stored grant may have been revoked' : ''; throw new Error(`${response.status} ${JSON.stringify(body)}${hint}`); } return body; } // The catalog is paged. Stopping at the first page would report a preset the // deployment does serve as unavailable, which is the one thing setup must not do. export async function allPresets(env = process.env) { const out = []; let cursor = ''; do { const page = await request( `/trigger-presets?page_size=100${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ''}`, {}, env, ); out.push(...(page.results ?? [])); cursor = page.has_more ? (page.next_cursor ?? '') : ''; } while (cursor); return out; } // The catalog answers in two shapes, so reading it takes two calls: the list // resolves the configured id, and the per-preset read is the only one carrying // `inputs`. Anything reasoning about inputs must be handed this, not a list entry. export async function readPreset(presetId, env = process.env) { const body = await request( `/trigger-presets/${encodeURIComponent(presetId)}`, {}, env, ); if (!body.trigger_preset) { throw new Error( `GET /trigger-presets/${presetId} returned no trigger_preset.`, ); } // Identity from the id we resolved, so a narrower body than today's cannot // register a trigger with preset_id: undefined. return { preset_id: presetId, ...body.trigger_preset }; } // Backward-looking: it searches indexed history, while delivery only fires // forward from a trigger's creation. A match older than the trigger is real and // still never arrives. export async function searchPresetEvents( presetId, inputs = {}, pageSize = 5, env = process.env, ) { return request( `/trigger-presets/${encodeURIComponent(presetId)}/events/search`, { method: 'POST', body: JSON.stringify({ inputs, page_size: pageSize }) }, env, ); } export async function listTriggers(env = process.env) { const out = []; let cursor = ''; do { const page = await request( `/triggers?page_size=100${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ''}`, {}, env, ); out.push(...(page.results ?? [])); cursor = page.has_more ? (page.next_cursor ?? '') : ''; } while (cursor); return out; } ``` ### Discover and register one trigger Select a supported preset, read its authoritative inputs, resolve the meeting configuration, and create a webhook delivery while saving only the returned trigger identifiers locally. scripts/setup-trigger.mjs ``` #!/usr/bin/env node import { loadEnv, writeEnv } from '../lib/config.mjs'; import { allPresets, readPreset, request } from '../lib/glean-api.mjs'; import { assertOffset, resolveInputs, selectPreset } from '../lib/presets.mjs'; loadEnv(); const webhookUrl = process.env.GLEAN_WEBHOOK_URL?.trim(); const bearer = process.env.GLEAN_WEBHOOK_BEARER_TOKEN?.trim(); const datasource = process.env.GLEAN_TRIGGER_DATASOURCE || 'googlecalendar'; const presetId = process.env.GLEAN_TRIGGER_PRESET_ID?.trim(); const offset = process.env.GLEAN_TRIGGER_OFFSET_SECONDS ?? '1800'; if (!webhookUrl || !webhookUrl.startsWith('https://')) { throw new Error( 'Set GLEAN_WEBHOOK_URL to the public HTTPS URL ending in /webhook.', ); } if (!bearer) throw new Error('Set GLEAN_WEBHOOK_BEARER_TOKEN in .env.'); if (process.env.GLEAN_TRIGGER_ID) { throw new Error( 'GLEAN_TRIGGER_ID is already set; delete it before creating another trigger.', ); } const listed = selectPreset(await allPresets(), presetId, { datasource, envVar: 'GLEAN_TRIGGER_PRESET_ID', }); const preset = await readPreset(listed.preset_id); const offsetSeconds = offset === 'none' || offset === '' ? undefined : offset; assertOffset(preset, offsetSeconds); const inputs = resolveInputs(preset, process.env, { time_offset: offsetSeconds, }); const body = await request('/triggers', { method: 'POST', body: JSON.stringify({ preset_id: preset.preset_id, inputs, delivery: { webhook_url: webhookUrl, auth: { type: 'BEARER', secret: bearer }, }, }), }); const trigger = body.trigger; if (!trigger?.trigger_id || !trigger.signing_secret) { if (trigger?.trigger_id) { await request(`/triggers/${encodeURIComponent(trigger.trigger_id)}`, { method: 'DELETE', }); } throw new Error('Trigger creation returned no trigger id or signing secret.'); } writeEnv({ GLEAN_TRIGGER_ID: trigger.trigger_id, GLEAN_WEBHOOK_SIGNING_SECRET: trigger.signing_secret, }); console.log(`Created ${preset.preset_id} (${preset.display_name}).`); console.log(`Trigger ID saved to .env: ${trigger.trigger_id}`); console.log('The signing secret is saved locally and will not be printed.'); ``` ### Verify signed webhook delivery Validate bearer delivery auth, timestamp freshness, and the Standard Webhooks signature before accepting a matching meeting event. lib/signature.mjs ``` import crypto from 'node:crypto'; export const demoSecret = 'whsec_Z2xlYW4tY29va2Jvb2stbWVldGluZy1wcmVw'; function keyFor(secret) { if (!secret.startsWith('whsec_')) { throw new Error('Standard Webhooks secrets must start with whsec_.'); } return Buffer.from(secret.slice(6), 'base64'); } export function sign(secret, webhookId, timestamp, body) { return crypto .createHmac('sha256', keyFor(secret)) .update(`${webhookId}.${timestamp}.${body}`) .digest('base64'); } export function parseSignatureHeader(value = '') { return value .trim() .split(/\s+/u) .map((part) => part.split(',', 2)) .filter(([version, signature]) => version === 'v1' && signature) .map(([, signature]) => signature); } export function verifySignature({ secret, webhookId, timestamp, body, signatures, }) { if (!secret.startsWith('whsec_')) return false; const expected = Buffer.from(sign(secret, webhookId, timestamp, body)); return signatures.some((candidate) => { const actual = Buffer.from(candidate); return ( actual.length === expected.length && crypto.timingSafeEqual(actual, expected) ); }); } ``` Architecture Customer calendarA matching customer-meeting event ![](/img/cookbook/glean-trigger.svg)Glean Platform TriggerPreset discovery, registration, and signed delivery Prep receiverPrints a focused request without writing downstream Prerequisites Node 22.12 or newer The Platform Triggers API enabled for the tenant A calendar trigger preset that advertises a time\_offset, normally GCAL\_1 A public HTTPS URL for the receiver, such as a temporary cloudflared tunnel A bearer token that Glean may send to the receiver Run it yourself 1 **Scaffold the recipe** ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/customer-meeting-prep-trigger customer-meeting-prep-trigger ``` 2 **Install dependencies** This recipe uses Node's built-in HTTP server and fetch; there is no external automation platform or downstream write dependency. ``` cd customer-meeting-prep-trigger && npm install ``` 3 **Run the fixture verification** Verify preset selection, experimental Platform API headers, input resolution, and Standard Webhooks signing before connecting a tenant. ``` npm run verify:fixture ``` 4 **Sign in to Glean** Use @gleanwork/auth for tenant discovery and refreshable OAuth credentials stored outside the project. If OAuth is unavailable, provide a user-scoped token with the Triggers permission as a CI fallback. ``` npm run login -- --email "" ``` 5 **Configure the meeting pattern and receiver** Set GLEAN\_TRIGGER\_INPUT\_TITLE to a distinctive title substring such as Customer QBR, choose the preset ID returned by the catalog, and set GLEAN\_WEBHOOK\_URL to a public HTTPS URL ending in /webhook. Set a bearer token that the receiver will accept. 6 **Start and expose the receiver** The receiver listens on loopback, verifies bearer delivery auth and the Standard Webhooks signature, and prints a prep request for matching meeting titles. Expose /webhook through a public HTTPS tunnel before registering the trigger. ``` npm start ``` 7 **Register the Platform Trigger** Setup reads GET /api/trigger-presets and the selected preset detail, then creates one trigger through POST /api/triggers. It saves the returned trigger ID and signing secret locally. ``` npm run setup ``` 8 **Test one signed delivery** Send one fixture delivery through the same signed webhook contract without waiting for a calendar event. Then schedule one real matching meeting to verify forward delivery from the Platform Trigger. ``` npm run test:webhook ``` 9 **Delete the trigger** Delete only the trigger ID created by this recipe and clear the local signing secret. ``` npm run delete ``` One trigger, one receiver The quickstart creates one trigger and prints one prep request; it does not connect a tracker, Slack, Cursor, or n8n. Verify before acting The receiver verifies bearer auth, timestamp freshness, and the Standard Webhooks signature before inspecting the event. A public HTTPS endpoint is required Glean cannot deliver to localhost directly. Use a temporary HTTPS tunnel for the beginner walkthrough and keep the URL private. Preset availability is tenant-specific The recipe refuses to guess a preset ID and only registers a preset returned by the connected tenant's catalog. Take it further * Pass the meeting and account context to the Triage or Account Brief Agent after the delivery contract is reliable. * Add a read-only preview that gathers recent account changes before writing anything downstream. * Use the same receiver pattern with a different tenant-supported content-event preset. Try it **Deliver one signed event for a customer meeting whose title matches the configured pattern.** The receiver verifies the bearer token and Standard Webhooks signature, prints a Customer meeting prep request with the title, start time, and event URL, and does not call any downstream system. **Delete the trigger after the test.** The recipe deletes only the trigger ID saved by its own setup command and clears the local signing secret. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:customer-meeting-prep-trigger ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/customer-meeting-prep-trigger) Runs the recipe through the Glean cookbook plugin. [Triggers](/cookbook.md#triggers) collection 1. 1Trigger a customer meeting prep requestBeginner Auth Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (TRIGGERS). At a glance CapabilitiesWorkflows SurfacesPlatform API StatusQuickstart Time\~20 min Required scopes TRIGGERS --- [Agents](/cookbook.md#agents) [ · ](/cookbook.md#agents) [2](/cookbook.md#agents) [ of](/cookbook.md#agents) [ ](/cookbook.md#agents) [3](/cookbook.md#agents) # Approve an agent action from a CLI Start a durable agent run from TypeScript, see the exact tool call it wants to make, and approve or reject it before it happens. The agent only sends you a Slack DM. \~5 minBeginner Problem A tool can change something outside Glean before anyone has a chance to review it. A durable agent run stops when the agent reaches a tool that needs confirmation, stores the exact call, and waits. Your application reads the call, shows it to a person, and sends their decision; the same run then continues. This recipe walks that flow with a small TypeScript CLI and an agent whose only tool sends you a Slack DM. API flow An approval takes four calls, and each one needs a value from an earlier response. Keep the `run_id` from the start, poll until the run needs a decision, and answer with the `interaction_id` the person reviewed. These are the bodies from one approved run, with example IDs and empty messages left out. 1. Step 1POSTcreateRun 2. Step 2GETgetRun 3. Step 3POSTrespondToRun 4. Step 4GETgetRun * `run_id`returned by step 1 , used by steps 2, 3, 4 * `interaction_id`returned by step 2 , used by step 3 - `agent_id` The published agent's ID: the 32-character ID after /agents/ in its Agent Builder URL. - `decision` APPROVE or REJECT, chosen by the person who reviewed the tool call. Step 1 of 4 PreviousNext ### Start a durable run POST`/api/agents/{agent_id}/runs``glean.agents.createRun` Send the message for the agent. The response is the new run, still working. * You supply `agent_id` * Returns `run_id`used by steps 2, 3, 4 Request body ``` { "execution_mode": "DURABLE"1, "stream": false2, "messages": [ { "role": "USER", "content": [ { "type": "text", "text": "Cookbook approval test 2026-09-29T21:51:07.008Z" } ] } ]} ``` Response body ``` { "run": { "run_id": "5d0c9e1f7a2b4c6e8f1a3b5c7d9e0f12"3, "agent_id": "3a2139bdf60540248c270d77887053f0", "state": "RUNNING", "created_at": "2026-09-29T21:51:07.204Z", "updated_at": "2026-09-29T21:51:07.204Z", "pending_interactions": [] }, "request_id": "b7e41f0c2d9a4e6f8a1c3e5b7d9f0a24"} ``` 1. 1Request `execution_mode`DURABLE keeps the run on the server, so it can stop and wait for a person. 2. 2Request `stream`false returns the run as one JSON body instead of a stream. 3. 3Response `run.run_id`Keep this. Every later call names the run by it. Architecture TypeScript CLIStarts a durable run and polls it YouReview the exact tool call and approve or reject it Glean agentResumes the same run and acts only if approved Your Slack DMsThe one place the agent can write Prerequisites Node.js 22.12+ and npm A Glean instance with durable agent runs available Permission to create agents in Agent Builder The **Slack Actions** tool pack available in Agent Builder, and your Slack account connected in Glean, so the agent can send you a direct message Permission to sign in with the `agents` scope Steps 1 **Copy the project** ``` npx -y tiged@2.12.8 gleanwork/glean-cookbook/recipes/human-in-the-loop-agent human-in-the-loop-agent ``` 2 **Install and run the offline tests** Run every later command from this directory in the same shell. The tests run the real SDK against local HTTP handlers; they don't sign in or contact Glean or Slack. ``` cd human-in-the-loop-agent && npm ci npm test ``` 3 **Create the agent and paste its instructions** In Glean, open **Agents** from the left navigation and click **Create agent**. **Auto mode** is selected by default, and **Builder Assistant** opens beside the agent; you don't need it (if it asks you to describe the agent first, paste these instructions into it). Click the name at the top of the builder and rename it `Cookbook approval demo`. Replace any text under **Instructions** with the text this command prints. ``` cat agent-instructions.txt ``` 4 **Add the Slack tool** In the **Capabilities** tab, under **Tools**, search for **Slack Actions** and add it. Under **Write tools**, keep only **Send Slack message to user**. Leave **Allow agent to use write tools without approval** unchecked: that checkbox is the approval boundary this recipe demonstrates. A spec file can't set this up for you, because it refers to Slack by a tool provider ID that is different on every Glean instance. 5 **Set the trigger and publish** In the **Triggers** tab, set **When should the agent run?** to **Manually run** and **What type of input does it need?** to **Chat message**. Click **Publish**. Until you publish, your edits are only a draft, and API runs use the published agent. 6 **Copy the agent ID** Click **Share** at the top right of the agent. Under **Publishing options**, the **API** section shows the **Agent ID** with a copy button; use it as `` below. You don't need **Create token** there: the next step signs you in instead. The ID is also the 32-character value after `/agents/` in the page URL. 7 **Sign in** Complete browser sign-in yourself. `glean-auth` finds your Glean backend from your email and keeps a refreshable session outside this project, so a run can wait for you. If OAuth isn't available, copy `.env.example` to `.env` and set `GLEAN_API_TOKEN` to a user-scoped token with the `agents` scope instead. Never paste a token into a chat or a command. ``` npm run login -- --email "" ``` 8 **Run the agent and decide** The command starts one durable run, waits for the agent to pause, and shows the tool, what it does, and its exact arguments. Type `a` to approve that one call (the DM arrives), `r` to reject it (nothing is sent), `c` to cancel the run, or press **Enter** to leave it waiting. If the run finishes without asking, the Slack tool isn't added, **Allow agent to use write tools without approval** is checked, or the agent wasn't published. ``` npm start -- --agent-id "" --email "" --show-json ``` 9 **Follow the API calls** `--show-json` prints each API request and response body under the SDK call that sent it, so you can follow `run_id` and `interaction_id` from one call to the next. Leave it off to see only the review. The JSON includes the tool arguments, so keep it out of shared logs. 10 **Pick a waiting run back up** Closing the CLI or reaching the 120-second wait doesn't cancel the run; the CLI prints the full resume command, with the agent and run IDs. After a decision, a resumed run can take a few minutes to finish; if the wait runs out, resume again. Without a terminal to ask in, resume prints a decision command bound to the pending interaction ID; run it only after a person has reviewed that call. 11 **Check approve, reject, and cancel** Approve one run and confirm exactly one DM arrives. Run the command again and reject; confirm no DM arrives and the agent says the message was not sent. Run it a third time, press **Enter**, then run the resume command it printed and cancel the run (`c`); it ends cancelled with nothing pending. Only Slack can show whether a message was sent. ``` npm start -- --agent-id "" --email "" --show-json ``` 12 **Clean up** Delete or archive the `Cookbook approval demo` agent in Agent Builder when you're done. Nothing here deletes runs, messages, or agents. Keep the approval boundary on the tool Leave Allow agent to use write tools without approval unchecked for the tool you want reviewed. Asking for consent in the agent instructions is not an approval gate. If a run finishes without pausing, treat it as a configuration error. Decide on the call you showed Send the interaction ID the person reviewed, never a newly polled one. One decision covers one call: if the agent pauses again, show the new call. A decision cannot edit arguments; reject and start again instead. Never approve without a person Nothing in this recipe approves automatically. Without a terminal to ask in, the CLI prints a decision command bound to the pending interaction ID; a coding assistant must still ask the user before running it. Only count input given after the call is on screen: the CLI throws away anything typed while it waited, so a keypress made earlier cannot approve a call nobody has seen. Don't retry a start Retrying a start after an unknown outcome creates a second run, so the client never retries on its own. After a network error, read the run before doing anything else. Keep credentials and printed tool arguments out of shared logs. One pending call at a time The CLI reviews a single tool approval and refuses a run waiting on more than one. Multi-call approval batches are outside this recipe. The agent is built by hand Agent spec files refer to built-in tools such as Slack by a tool provider ID that differs on every Glean instance, and an unresolved tool is dropped without an error. Until that's portable, creating the agent in Agent Builder is the reliable path. Durable is not crash recovery A run survives your connection closing, not a server worker crashing. An active turn times out after 30 minutes; runs waiting for approval don't expire through that timeout. Cancelling is not undo Cancellation stops future work. A tool call that already ran stays done, and closing the CLI never cancels a run. Take it further * Replace the terminal prompt with an approval screen in your application. Keep the reviewed interaction ID and arguments attached to the decision. * Swap in another write tool once you have confirmed it requires confirmation. Show large numeric arguments from the raw response, since JSON.parse rounds integers above 2^53. Try it **Start a run and approve the Slack DM it asks to send.** The run pauses in REQUIRES\_INPUT with one TOOL\_APPROVAL for Send Slack message to user, showing the message text, and nothing arrives in Slack yet. After approval the same run reaches SUCCEEDED and exactly one DM arrives. **Start a run and reject the Slack DM it asks to send.** The run pauses before sending. After rejection the same run reaches SUCCEEDED, the agent replies that the message was not sent, and no DM arrives. **Start a run, leave it waiting, then resume it and cancel.** Leaving the prompt sends nothing and keeps the run in REQUIRES\_INPUT. Resuming shows the same pending call; cancelling ends the run CANCELLED with no pending interactions and no DM. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:human-in-the-loop-agent ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/human-in-the-loop-agent) Runs the recipe through the Glean cookbook plugin. [Agents](/cookbook.md#agents) collection 1. 1[Triage one support issue with a Glean Agent](/cookbook/triage-support-issue.md)Beginner 2. 2Approve an agent action from a CLIBeginner 3. 3[On-call Copilot](/cookbook/oncall-copilot.md)Advanced Auth Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (agents). At a glance CapabilitiesAgents, Tools SurfacesPlatform API, Agents, Tools, API clients StatusQuickstart Time\~5 min Required scopes agents --- [Skills](/cookbook.md#skills) [ · ](/cookbook.md#skills) [2](/cookbook.md#skills) [ of](/cookbook.md#skills) [ ](/cookbook.md#skills) [3](/cookbook.md#skills) # Import a skill from GitHub Preview a public GitHub skill on a branch URL with the official TypeScript API client, import the selected URL, sync that captured skill, confirm it with get and list, then delete only IDs this run created. \~20 minIntermediate Problem Some teams keep Agent Skills in GitHub rather than a local SKILL.md. Previewing that URL, importing only the selected skills, and syncing a captured ID is a different job from the first persist or name-based versioning. This recipe uses a public GitHub skill directory on the main branch so the run is answerable on the reader's tenant without a seeded corpus. Code walkthrough The runnable scaffold calls the Skills API through the official SDK, previews a GitHub URL without persisting a source, imports only selected URLs, and syncs one captured skill. ### Configure the Skills API client Resolve the reader's tenant and use the native Skills scopes or the recorded legacy compatibility mode. src/client.ts ``` import fs from 'node:fs/promises'; import path from 'node:path'; import { Glean, type SDKOptions } from '@gleanwork/api-client'; import { createGleanTokenProvider, discoverGleanTenant } from '@gleanwork/auth'; const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']); const SCOPE_MODE_FILE = '.glean-scope-mode'; export interface GleanClientTarget { email?: string; serverUrl?: string; } async function loadDotEnv() { let text: string; try { text = await fs.readFile(path.join(process.cwd(), '.env'), 'utf8'); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return; throw error; } for (const raw of text.split(/\r?\n/)) { const line = raw.trim(); if (!line || line.startsWith('#')) continue; const eq = line.indexOf('='); if (eq <= 0) continue; const key = line.slice(0, eq).trim(); let value = line.slice(eq + 1).trim(); if ( (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")) ) { value = value.slice(1, -1); } if (process.env[key] === undefined) process.env[key] = value; } } async function resolveServerUrl({ email, serverUrl }: GleanClientTarget) { const explicit = serverUrl?.trim(); if (explicit) return explicit; const workEmail = email?.trim(); if (workEmail) return (await discoverGleanTenant(workEmail)).serverUrl; const configured = process.env.GLEAN_SERVER_URL?.trim(); if (configured) return configured; throw new Error( 'Pass --email or --server-url, or set GLEAN_SERVER_URL in your environment.', ); } async function configuredScopes(log: (message: string) => void) { const envMode = process.env.GLEAN_SKILLS_SCOPE_MODE?.trim(); let fileMode: string | undefined; try { fileMode = ( await fs.readFile(path.join(process.cwd(), SCOPE_MODE_FILE), 'utf8') ).trim(); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; } if (envMode && fileMode && envMode !== fileMode) { log( `GLEAN_SKILLS_SCOPE_MODE=${envMode} overrides .glean-scope-mode (${fileMode}).`, ); } else if (envMode) { log(`Using GLEAN_SKILLS_SCOPE_MODE=${envMode}.`); } else if (fileMode) { log(`Using .glean-scope-mode (${fileMode}).`); } else { log('Using native skills:read and skills:write scopes.'); } const mode = envMode || fileMode; return mode === 'legacy' ? ['SKILLS'] : ['skills:read', 'skills:write']; } export async function createGleanClient( target: GleanClientTarget, log: (message: string) => void = () => undefined, ) { await loadDotEnv(); const serverURL = await resolveServerUrl(target); const server = new URL(serverURL); const loopback = LOOPBACK_HOSTS.has(server.hostname); if ( (server.protocol !== 'https:' && !loopback) || server.username || server.password || server.search || server.hash || (server.pathname && server.pathname !== '/') || (!loopback && server.port) ) { throw new Error('Use a complete Glean backend HTTPS origin.'); } const staticToken = process.env.GLEAN_API_TOKEN?.trim(); let apiToken: string | ReturnType; if (staticToken) { log('Using GLEAN_API_TOKEN from the environment.'); apiToken = staticToken; } else { const scopes = await configuredScopes(log); log(`Using the OAuth session (${scopes.join(', ')}).`); apiToken = createGleanTokenProvider({ serverUrl: server.origin, scopes, }); } const options = { serverURL: server.origin, apiToken, timeoutMs: 30_000, retryConfig: { strategy: 'backoff', backoff: { initialInterval: 500, maxInterval: 5_000, exponent: 2, maxElapsedTime: 90_000, }, retryConnectionErrors: true, }, } satisfies SDKOptions; return new Glean(options); } ``` ### Preview, import, and sync Call previewSource with stream false by default, import the selected GitHub URL, sync that captured skill, and delete only IDs this run created. src/workflow\.ts ``` import type { Glean } from '@gleanwork/api-client'; import { GleanBaseError, PlatformProblemDetailError, } from '@gleanwork/api-client/models/errors'; import { PreviewSourceAcceptEnum } from '@gleanwork/api-client/sdk/skills.js'; import type { PlatformSkillSourcePreviewResponse } from '@gleanwork/api-client/models/components'; import { CleanupFailedError, formatCliError } from './errors.js'; import { DEFAULT_SOURCE_URL } from './fixture.js'; import { parsePreviewResult } from './preview.js'; export type SkillsApi = Pick< Glean['skills'], 'delete' | 'import' | 'list' | 'previewSource' | 'retrieve' | 'sync' >; export interface ImportResult { ids: string[]; displayName: string; sourceUrl: string; commitSha: string; updated: boolean; } function rethrow(error: unknown): never { throw error instanceof Error ? error : new Error('Verification failed.'); } export function cleanupCommand( skillId: string, auth: { email?: string; serverUrl?: string } = {}, ) { const parts = [`npm start -- cleanup --id ${skillId} --yes`]; if (auth.serverUrl?.trim()) { parts.push(`--server-url ${auth.serverUrl.trim()}`); } else if (auth.email?.trim()) { parts.push(`--email ${auth.email.trim()}`); } else { parts.push('--email '); } return parts.join(' '); } export function importedSuccessLine(result: ImportResult) { return `Imported ${result.displayName} (${result.ids.join(', ')}) from ${result.sourceUrl} at ${result.commitSha}; cleanup completed.`; } export function githubFetchStatusHint(status?: number) { if (status === 400) { return ' HTTP 400 means this GitHub URL or ref is not supported. Commit permalinks and 40-character SHA URLs are rejected. Use a branch or tag URL such as .../tree/main/....'; } if (status === 503) { return ' HTTP 503 means GitHub import is disabled or unavailable.'; } if (status === 403) { return ' HTTP 403 means this credential cannot import from GitHub.'; } if (status === 429) { return ' HTTP 429 means GitHub import is rate-limited. Wait and retry.'; } return ''; } function githubFetchError(error: unknown): Error { const summary = formatCliError(error).error; const status = error instanceof PlatformProblemDetailError ? error.status : error instanceof GleanBaseError ? error.statusCode : undefined; return new Error( `GitHub import failed: ${summary}.${githubFetchStatusHint(status)} The import recipe fails rather than skipping.`, ); } function isSkillsApiNotFound(error: unknown) { if (error instanceof PlatformProblemDetailError) return error.status === 404; if (error instanceof GleanBaseError) return error.statusCode === 404; return false; } function reraiseSourceError(error: unknown): never { if (isSkillsApiNotFound(error)) throw error; if ( error instanceof PlatformProblemDetailError || error instanceof GleanBaseError ) { throw githubFetchError(error); } throw error instanceof Error ? error : new Error('Verification failed.'); } export async function findSkillById(api: SkillsApi, skillId: string) { let cursor: string | undefined; do { const page = await api.list(100, cursor); if (page.skills.some((skill) => skill.id === skillId)) return true; cursor = page.next_cursor ?? undefined; } while (cursor); return false; } export async function deleteCapturedIds( api: SkillsApi, ids: string[], log: (message: string) => void, ) { const remaining: string[] = []; for (const id of ids) { log(`Deleting run-owned skill ${id}...`); try { await api.delete(id); } catch { remaining.push(id); } } return remaining; } export async function resolvePreview( api: SkillsApi, sourceUrl: string, stream: boolean, log: (message: string) => void = () => undefined, ): Promise { try { const preview = await api.previewSource( { source_url: sourceUrl, stream }, stream ? { acceptHeaderOverride: PreviewSourceAcceptEnum.textEventStream } : undefined, ); return parsePreviewResult(preview, log); } catch (error) { reraiseSourceError(error); } } export async function importSkillFromGithub( api: SkillsApi, options: { sourceUrl?: string; stream?: boolean; cleanup: boolean; auth?: { email?: string; serverUrl?: string }; log?: (message: string) => void; }, ): Promise { const log = options.log ?? (() => undefined); const sourceUrl = options.sourceUrl?.trim() || DEFAULT_SOURCE_URL; const createdIds: string[] = []; let result: ImportResult | undefined; let workError: unknown; try { log(`Previewing ${sourceUrl} without persisting a source...`); const preview = await resolvePreview( api, sourceUrl, options.stream === true, log, ); const selected = preview.skills.at(0); if (!selected) { const failures = preview.failures .map((failure) => `${failure.code}: ${failure.detail}`) .join('; '); throw new Error( failures ? `GitHub preview returned no importable skills (${failures}).` : 'GitHub preview returned no importable skills.', ); } log(`Importing ${selected.source_url}...`); let imported; try { imported = await api.import({ source_urls: [selected.source_url] }); } catch (error) { reraiseSourceError(error); } const skill = imported.skills.at(0); if (!skill) { throw new Error('Import returned no skills.'); } createdIds.push(...imported.skills.map((item) => item.id)); log('Confirming get and list return the imported skill...'); const retrieved = await api.retrieve(skill.id); if (retrieved.skill.id !== skill.id) { throw new Error('Direct retrieval returned a different skill.'); } if (!(await findSkillById(api, skill.id))) { throw new Error('List did not include the skill this run just imported.'); } log(`Syncing imported skill ${skill.id} from its stored GitHub URL...`); let synced; try { synced = await api.sync(skill.id); } catch (error) { reraiseSourceError(error); } result = { ids: [...createdIds], displayName: retrieved.skill.display_name, sourceUrl: selected.source_url, commitSha: synced.commit_sha, updated: synced.updated, }; } catch (error) { workError = error; } const remaining = options.cleanup ? await deleteCapturedIds(api, createdIds, log) : []; if (remaining.length > 0) { throw new CleanupFailedError( remaining, remaining.map((id) => cleanupCommand(id, options.auth)).join('\n '), workError, ); } if (workError) rethrow(workError); if (!result) throw new Error('Verification did not produce a result.'); return result; } ``` Architecture ![](/img/cookbook/github.svg)Public GitHub URLskill-creator on main Source previewno persisted source Skills importselected URLs only Syncrefresh one captured ID Prerequisites Node.js 22.12.0 or newer A Glean instance with Skills enabled Your work email, or the complete Glean backend HTTPS origin A tenant that permits the native skills:read and skills:write OAuth scopes; the legacy SKILLS compatibility scope or a user-scoped token is the fallback Tenant-side GitHub source fetching enabled for Skills; the default source is the public skill-creator directory on main, and verification fails rather than skipping if the tenant cannot preview it Run it yourself 1 **Copy the project onto your machine** Copy the runnable TypeScript GitHub import CLI and credential-free MSW fixture tests into a new directory. OAuth login and secure token storage come from the pinned @gleanwork/auth package. ``` npx -y tiged@2.12.8 gleanwork/glean-cookbook/recipes/import-skill-from-github import-skill-from-github ``` 2 **Install dependencies** ``` cd import-skill-from-github && npm install ``` 3 **Run the fixture tests** Run Vitest with recorded preview payloads and MSW, without GitHub or Glean credentials, covering JSON preview, optional SSE preview, import, sync, and captured-ID cleanup. ``` npm test ``` 4 **Sign in with OAuth** Discover your Glean backend from work email and request skills:read and skills:write. Only a recognized scope-grant failure triggers one retry with legacy SKILLS. If OAuth is not available, skip this command: copy .env.example to .env and fill GLEAN\_API\_TOKEN and GLEAN\_SERVER\_URL. ``` npm run login -- --email "" ``` 5 **Pass an explicit backend if you need one** If email discovery is wrong, pass --server-url with the complete Glean backend HTTPS origin on login, verify, and start. If DCR is restricted, export GLEAN\_OAUTH\_CLIENT\_ID in your shell before npm run login. npm run login does not read .env, so do not store that client id only in .env. 6 **Verify against your instance** Preview the public GitHub skill-creator directory on main, import the selected URL, sync that captured skill, confirm get and list, then permanently delete only IDs this run created. HTTP 400 means an unsupported GitHub URL or ref, including commit permalinks. HTTP 503 means GitHub import is disabled or unavailable. HTTP 403 means this credential cannot import. HTTP 429 means rate-limiting. Success prints an Imported line that ends with cleanup completed. ``` npm run verify -- --email "" ``` 7 **Watch repository scan progress** Run the same preview, import, and sync path with --stream. Scan events print as the tenant walks the GitHub directory. This command still deletes the captured skill when it finishes; there is no keep path. Pass --yes when the terminal is not interactive. ``` npm start -- --email "" --yes --stream ``` Preview before import Inspect the GitHub URL with previewSource first. Preview persists neither a source nor any skill. Import selected URLs only Pass source URLs returned by preview. Import is atomic: if any selected URL cannot be fetched, validated, or persisted, no skills are created. Fail when GitHub is unreachable If this tenant cannot fetch GitHub, stop. Do not skip verification or treat an empty preview as success. Delete by captured ID only Cleanup deletes only IDs this run created. Never choose a cleanup target by display name or a broad catalog search. If delete fails, do not report cleanup completed. GitHub must be fetchable Preview, import, and sync require GitHub import. The default source is a branch URL because commit permalinks are unsupported. HTTP 400 means an unsupported URL or ref. HTTP 503 means GitHub import is disabled or unavailable. HTTP 403 means this credential cannot import. HTTP 429 means rate-limiting. Any of those statuses is a failed verify, not a skipped one. OAuth policy varies by tenant Native Skills scopes may not yet be grantable everywhere. The login wrapper uses the legacy compatibility scope only for a recognized scope-grant failure; a user-scoped token remains an explicit fallback. Take it further * Pass --source-url to preview a different public GitHub skill directory after the default branch source succeeds. * Use the Intermediate publishing recipe when the source of truth is a local bundle and you need version supersession or zip sandboxing. * Call glean.skills.createVersion() or PATCH enable/disable only after you already have a captured skill ID. Try it **Preview, import, and sync the public GitHub skill-creator fixture, then delete run-owned IDs** Stdout ends with Imported skill-creator (\) from the branch GitHub URL at \; cleanup completed. If GitHub import fails, the run fails with a GitHub-import error instead of skipping. HTTP 400 means an unsupported URL or ref, including commit permalinks. HTTP 503 means GitHub import is disabled or unavailable. HTTP 403 means the credential cannot import. HTTP 429 means rate-limiting. If delete fails, the process exits non-zero and does not print that success line. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:import-skill-from-github ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/import-skill-from-github) Runs the recipe through the Glean cookbook plugin. [Skills](/cookbook.md#skills) collection 1. 1[Validate skill publishing](/cookbook/validate-and-publish-skill.md)Beginner 2. 2Import a skill from GitHubIntermediate 3. 3[Publish a versioned skill bundle](/cookbook/skill-publishing-pipeline.md)Intermediate Auth Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (skills:read, skills:write). At a glance CapabilitiesSkills SurfacesPlatform API StatusProduction pattern Time\~20 min Required scopes skills:readskills:write --- [Complete apps](/cookbook.md#complete-apps) [ · ](/cookbook.md#complete-apps) [1](/cookbook.md#complete-apps) [ of](/cookbook.md#complete-apps) [ ](/cookbook.md#complete-apps) [5](/cookbook.md#complete-apps) # Build an IT helpdesk page in Lovable Paste a prompt into Lovable to get a private page that answers common IT questions from your Glean docs. \~45 minBeginner Problem Half the helpdesk tickets are "how do I reset my SSO password" and "how do I get a new laptop." The IT docs already answer both. This recipe builds a private Lovable page that answers those questions from your Glean content before a ticket gets filed. Prerequisites A Lovable account A Glean Client API token with the CHAT scope. Create it in Admin Console → API access → Client API tokens, and keep it as a Lovable backend secret. Every request uses that token owner's access A private Lovable project. Multi-user deployments require authenticated per-user Glean OAuth Steps 1 **Copy your instance name** Copy the instance name from the lookup on this page, or from `https://app.glean.com/admin/about-glean`. For `https://acme-be.glean.com` that value is `acme`, not the full URL and not `app.glean.com`. Cookbook plugin users can run `resolve-backend.mjs` with the work email and use the `instance` field. 2 **Copy the prompt into a private Lovable project** Click `Copy Lovable prompt`. Replace `` with your instance name. Paste the result into a new private project at `https://lovable.dev`. Cookbook plugin users: the same text is in `lovable-prompt.md` next to this skill. 3 **Add secrets when Lovable asks** When Lovable asks, add `GLEAN_API_TOKEN` and `GLEAN_INSTANCE` as backend secrets, never in the chat. This token is one person's Glean access. Keep the project private. 4 **Ask the two IT questions** Ask "Where do I reset my SSO password?" and "How do I request a new laptop?" Each answer should cite a real doc from your own instance. Take it further * Scope the `CHAT` token even further if your Glean plan supports per-collection tokens, so this tool can only ever answer from IT content. * Compare this to [`no-code-pto-lookup-replit`](/cookbook/no-code-pto-lookup-replit.md). Same Chat API call, same constraint that the browser never holds the token, different no-code tool and persona. Try it **Where do I reset my SSO password?** A cited answer from your own IT docs, with a real title and URL. **How do I request a new laptop?** A cited answer from your own IT docs, with a real title and URL. Copy Lovable prompt[View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/no-code-it-helpdesk-lovable) Paste into a new private Lovable project. Replace \ first. [Complete apps](/cookbook.md#complete-apps) collection 1. 1Build an IT helpdesk page in LovableBeginner 2. 2[Build a PTO lookup page in Replit](/cookbook/no-code-pto-lookup-replit.md)Beginner 3. 3[Onboarding Hub: a day-one checklist grounded in your own docs](/cookbook/onboarding-hub.md)Intermediate 4. 4[Answer an RFP or security questionnaire](/cookbook/rfp-responder.md)Intermediate 5. 5[Customer 360: an account page built from your own content](/cookbook/customer-360.md)Intermediate Auth Create a Client API token with CHAT in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client). Keep it in the host's secret store, never in a prompt. Steps: [Glean-issued tokens](/api-info/client/authentication/glean-issued.md). At a glance CapabilitiesChat SurfacesClient API StatusShowcase Time\~45 min Required scopes CHAT --- [Complete apps](/cookbook.md#complete-apps) [ · ](/cookbook.md#complete-apps) [2](/cookbook.md#complete-apps) [ of](/cookbook.md#complete-apps) [ ](/cookbook.md#complete-apps) [5](/cookbook.md#complete-apps) # Build a PTO lookup page in Replit Paste a prompt into Replit Agent to get a private page that answers PTO and benefits questions from your Glean docs. \~45 minBeginner Problem HR fields the same PTO and benefits questions on repeat. The answers are already in your docs. This recipe builds a private Replit page that looks those answers up in Glean. Nobody on the team has to write a backend. Prerequisites A Replit account with Agent access A Glean Client API token with the CHAT scope. Create it in Admin Console → API access → Client API tokens, and keep it in Replit Secrets. Every request uses that token owner's access A private Repl. Multi-user deployments require authenticated per-user Glean OAuth Steps 1 **Copy your instance name** Copy the instance name from the lookup on this page, or from `https://app.glean.com/admin/about-glean`. For `https://acme-be.glean.com` that value is `acme`, not the full URL and not `app.glean.com`. Cookbook plugin users can run `resolve-backend.mjs` with the work email and use the `instance` field. 2 **Copy the prompt into a private Repl** Click `Copy Replit prompt`. Replace `` with your instance name. Paste the result into a new private Repl at `https://replit.com/new` and open Agent. Cookbook plugin users: the same text is in `replit-agent-prompt.md` next to this skill. 3 **Add secrets when Replit asks** When Replit Agent asks, add `GLEAN_API_TOKEN` and `GLEAN_INSTANCE` in the Secrets tab, never in the chat. This token is one person's Glean access. Keep the Repl private. 4 **Ask a PTO question, then an off-topic one** Ask "What is our PTO policy?" and confirm a cited answer from your own docs. Then ask "what's our revenue?" and confirm the assistant does not invent an answer when Glean cites nothing. Take it further * Scope the `CHAT` token even further if your Glean plan supports per-collection tokens, so this tool can only ever answer from HR content. * Compare this to [`no-code-it-helpdesk-lovable`](/cookbook/no-code-it-helpdesk-lovable.md). Same Chat API call, same constraint that the browser never holds the token, different no-code tool and persona. Try it **What is our PTO policy?** A cited answer from your own HR docs, with a real title and URL. **what's our revenue?** No fabricated answer when Glean cites nothing. The page shows that it does not know. Copy Replit prompt[View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/no-code-pto-lookup-replit) Paste into a new private Replit project. Replace \ first. [Complete apps](/cookbook.md#complete-apps) collection 1. 1[Build an IT helpdesk page in Lovable](/cookbook/no-code-it-helpdesk-lovable.md)Beginner 2. 2Build a PTO lookup page in ReplitBeginner 3. 3[Onboarding Hub: a day-one checklist grounded in your own docs](/cookbook/onboarding-hub.md)Intermediate 4. 4[Answer an RFP or security questionnaire](/cookbook/rfp-responder.md)Intermediate 5. 5[Customer 360: an account page built from your own content](/cookbook/customer-360.md)Intermediate Auth Create a Client API token with CHAT in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client). Keep it in the host's secret store, never in a prompt. Steps: [Glean-issued tokens](/api-info/client/authentication/glean-issued.md). At a glance CapabilitiesChat SurfacesClient API StatusShowcase Time\~45 min Required scopes CHAT --- [Complete apps](/cookbook.md#complete-apps) [ · ](/cookbook.md#complete-apps) [3](/cookbook.md#complete-apps) [ of](/cookbook.md#complete-apps) [ ](/cookbook.md#complete-apps) [5](/cookbook.md#complete-apps) # Onboarding Hub: a day-one checklist grounded in your own docs A guided first-week hub for new hires: a checklist with progress, and every step able to answer itself from your own onboarding content. \~45 minIntermediate ![Onboarding Hub with a first-week checklist and cited assistant guidance](/img/cookbook/previews/onboarding-hub/preview.webp)View preview Problem New hires land on day one with a dozen tasks spread across HR, IT, and their team — and no single place to see what's done, what's left, and where to get help. The Onboarding Hub solves that: a gamified checklist with progress tracking, milestone badges, and contextual Glean chat on every step. This recipe builds the hub two ways so you can pick the trade-off that fits your portal — Web SDK for fastest SSO integration, Platform Chat when you need full UI control. Architecture Onboarding Hubchecklist + progress + chat Path A: Web SDKrenderChat — Glean owns chat UI Path B: Platform Chatglean.chat.create — you own the UI Gleanpermission-aware cited answers Prerequisites Node 20.19+ or 22.12+ Your work email, so setup can find your Glean tenant Onboarding content already indexed in Glean, because the checklist answers from your own docs Web SDK path only: you are already signed in to Glean in your normal browser Steps 1 **Pick a path** Both paths build the same first-week checklist. Choose Web SDK if you want Glean's chat UI and the Glean session already in your browser. Choose Platform Chat if you want to own the answer UI and call Glean from your backend. * Web SDK * Platform Chat 1 **Copy the project onto your machine** Creates an `onboarding-hub` folder in whatever directory you run this from. Every command after this one runs inside that folder. ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/onboarding-hub/web-sdk onboarding-hub ``` 2 **Install dependencies** Installs the Glean Web SDK and the Vite dev server that hosts the page. ``` cd onboarding-hub && npm install ``` 3 **Find your Glean tenant** Your email is used once to find which Glean tenant you belong to. The command creates `.env.local` and fills in `VITE_GLEAN_BACKEND`. ``` npm run configure -- --email "" ``` 4 **Keep the sample checklist, or use your own** `public/steps.json` is already in the project, so you can run with the sample. To use your own onboarding program, replace those steps. Each one needs a unique `id`, a `title`, a `group` (`it`, `hr`, `team`, or `engineering`), `initiallyDone`, and an `askPrompt`. 5 **Open the page** Starts Vite and prints a Local URL. Open that URL yourself in your normal browser where you are already signed in to Glean. ``` npm run dev ``` 6 **Check it against your own content** Once the page is open in your signed-in browser, confirm your checklist renders. Click **Ask about this** on a step and check that Glean returns a cited first-day answer. 1 **Copy the project onto your machine** Creates an `onboarding-hub` folder in whatever directory you run this from. Every command after this one runs inside that folder. ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/onboarding-hub/platform-chat onboarding-hub ``` 2 **Install dependencies** Installs the Glean API client and the small local server that serves the page. ``` cd onboarding-hub && npm install ``` 3 **See it work before you connect anything** Runs the checklist and chat against recorded Sample Corp answers, so you can see cited first-day, VPN, and PTO replies plus unsupported-question escalation before you connect anything. This needs no Glean credentials. ``` npm run verify:fixture ``` 4 **Sign in to Glean** Your email is used once to find which Glean tenant you belong to, then a browser window opens for you to approve access. The command creates the `.env` file for you and fills in `GLEAN_SERVER_URL` and `GLEAN_API_TOKEN`. If your tenant has not enabled OAuth, skip this command and do it by hand instead: copy `.env.example` to `.env`, then fill in your Glean instance URL and a Glean API token that has the `CHAT` scope. ``` npm run login -- --email "" ``` 5 **Keep the sample checklist, or use your own** `steps.json` is already selected in `.env`, so you can run with the sample. To use your own onboarding program, replace those steps. Each one needs a unique `id`, a `title`, a `group` (`it`, `hr`, `team`, or `engineering`), `initiallyDone`, and an `askPrompt`. 6 **Check it against your own content** Takes 1 to 3 minutes. It starts its own server, checks your onboarding topics for cited answers, and fails if an unsupported question does not escalate. ``` npm run verify ``` 7 **Open the page** Starts the server and prints a Local URL. Open that URL in your browser. ``` npm start ``` Take it further * Wire real HRIS completion events instead of localStorage toggles. * Add a manager view that shows onboarding progress across the team. * Scope chat to onboarding-only documents, so a first-week question cannot wander into unrelated content. Try it **What should I do on my first day?** Returns a cited answer drawn from your own onboarding documents, and the checklist reflects steps that actually appear in them rather than a hardcoded list. **How do I set up VPN?** Returns a cited answer from your own IT documentation. **What's our PTO policy?** Returns a cited answer respecting the asker's permissions — the same question from two people with different access should not return content either of them can't see. **Ask about a step your docs don't cover** Platform Chat path only: the app offers its escalation affordance rather than inventing a plausible-sounding onboarding step. The Web SDK owns its unsupported-answer experience. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:onboarding-hub ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/onboarding-hub) Runs the recipe through the Glean cookbook plugin. [Complete apps](/cookbook.md#complete-apps) collection 1. 1[Build an IT helpdesk page in Lovable](/cookbook/no-code-it-helpdesk-lovable.md)Beginner 2. 2[Build a PTO lookup page in Replit](/cookbook/no-code-pto-lookup-replit.md)Beginner 3. 3Onboarding Hub: a day-one checklist grounded in your own docsIntermediate 4. 4[Answer an RFP or security questionnaire](/cookbook/rfp-responder.md)Intermediate 5. 5[Customer 360: an account page built from your own content](/cookbook/customer-360.md)Intermediate Auth **Web SDK** This path uses your existing Glean browser session. Find the web app URL on the About page. The API [server URL](/get-started/authentication.md#finding-your-server-url) is a different field. **Platform Chat** Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (CHAT). At a glance CapabilitiesChat, Embed SurfacesWeb SDK, Platform API StatusShowcase Time\~45 min Required scopes **Platform Chat** CHAT --- [Agents](/cookbook.md#agents) [ · ](/cookbook.md#agents) [3](/cookbook.md#agents) [ of](/cookbook.md#agents) [ ](/cookbook.md#agents) [3](/cookbook.md#agents) # On-call Copilot An on-call dashboard that takes an alarm to a proposed action and puts a person in front of that action. It names a cause only when a past incident backs it, turns away an approver who is neither on call nor the service owner, and escalates instead of approving itself when nobody answers. It runs on recorded incidents, so you can watch it refuse things before connecting it to anything. \~1.5 hrAdvanced ![On-call Copilot triaging an alert with evidence and an approval gate](/img/cookbook/previews/oncall-copilot/preview.webp)View preview Problem An alarm fires at 3am. The runbook, the review of the last time this happened, who owns the service, and what it depends on all exist in Glean already. They just sit in different systems, and you search them one at a time while the graphs get worse. The retrieval is the easy part. What makes a copilot safe to point at a production service is what it refuses to do, so that is what you build here. The gate turns away anyone who is neither on call nor the service owner. A proposal nobody approves in time escalates instead of quietly approving itself. An action the planner invented never reaches a human. The audit log records approvals, refusals, executions, failures, rejections, and escalations. Know the edges before you point it anywhere real. Live Search, Chat, and optional Agents calls hit your instance. The bundled alarm buttons always fire payments-service. Catalog and evidence lookups expect the sample URL layout under /services/, /runbooks/, and /incidents/. The three registered actions are simulated in-process, and the acting user is asserted rather than authenticated. Architecture PagerDuty webhookfilters by severity and watched service Service registryreads owner, on-call, and escalation from the catalog Gleanpermission-aware Search, Chat, and optional Agents Evidence checkpast incidents support cause, runbooks support procedure Approval gatewho may approve, expiry, escalation, and audit Governed actionsonly pre-registered action ids may run Prerequisites Node 20 or newer Nothing else. The walkthrough replays recorded Glean responses, so it needs no credentials and makes no network calls Only if you later point it at your own instance: an indexed service catalog, runbooks, and incident reviews, plus a work email for sign-in or a Glean API token with the SEARCH and CHAT scopes. The agent planner also needs the AGENTS scope and a conversational agent ID. Plan on editing the catalog query and parser first, since they read the sample corpus's layout Steps 1 **Copy the project onto your machine** Creates an oncall-copilot folder in whatever directory you are in, holding the server, the dashboard, and the recorded incidents. Stay in that same directory for the rest of the steps, since each command changes into the folder itself. ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/oncall-copilot oncall-copilot ``` 2 **Install dependencies** Installs the few packages the TypeScript server needs. Everything runs on your machine, and nothing is deployed anywhere. ``` cd oncall-copilot && npm install ``` 3 **See what it refuses before connecting anything** Runs the unit tests, then replays the recorded incidents through the whole flow and checks the refusals: the gate turns away an actor who is neither on call nor the service owner, an unapproved proposal escalates without executing, an action the planner invented is refused, and a code-changing action with no supported cause drops to filing a ticket. The audit log records each of those decisions. The command reads no credentials and makes no network calls. ``` npm run verify:fixture ``` 4 **Open the dashboard** Starts the local server and prints a Local URL. Open that URL in a browser. The dashboard replays the same recorded Glean responses, so there is nothing to sign in to. ``` npm start ``` 5 **Fire the alarms and test the gate** The trigger row has one button per alarm. PAY-2231 gets a probable cause and the past incident it rests on. PAY-2232 gets no cause, because no past incident matches it, and its proposed fix drops to filing a ticket. PAY-2233 has the agent name an action that is not in the registry, so it is refused before any approval card appears. For a refused approval, select PAY-2231 or PAY-2232 in the queue, switch Acting as to the person who is neither on call nor the owner, and approve anyway: the request comes back 403 and the audit log records it. To watch a proposal hand off without executing, force the approval on that same incident to expire. Relevance is not evidence Only a matching past incident may support a causal claim. Runbooks support procedures, not causes. Authorization is not authentication Live and fixture both restrict who may approve, but they trust the asserted actor (INCIDENT\_ACTOR or, in demo mode, X-Incident-Actor). A deployment must authenticate the actor and decide which credential executes actions. This recipe reports impersonation: false. Take it further * Persist incidents and the audit log. An audit log you can lose by restarting a process is not an audit log. * Add the rest of the dashboard: response-time rollups, an expiring-soon lane, and an end-of-shift handoff summary. * Replace the simulated actions with real governed tools, keeping the registry boundary and the approval log exactly where they are. * Write the postmortem draft back into your knowledge base. The next incident then retrieves it as precedent, and the evidence rules get better over time. Try it **Fire the sample canary alarm** Fire the canary alarm, PAY-2231. The incident joins the on-call queue, and the triage card names the service, its tier, the engineer on call, and the owner, all read from the recorded catalog. Probable cause appears with a supported badge and links to the past incident review it came from. In the evidence table that review is tagged precedent and the deploy runbook is tagged procedure. The approval gate offers Draft fix PR, and #eng-oncall shows the acknowledgement and the request for approval. **Triage an alert with no matching past incident** Fire the no-precedent alarm, PAY-2232. The triage card says no probable cause was asserted, because no past incident matches this alarm signature, and no Probable cause section appears. The deploy runbook still ranks highest in the evidence table, tagged procedure. The proposed action drops from Draft fix PR to File tracking ticket, and both #eng-oncall and the audit log record that downgrade and why. **Approve the proposed action as the outsider** Fire an alarm, switch Acting as to the person who is neither on call nor the owner, then click Approve & execute. The approval card warns before you click that this person is not on call and does not own the service, and names who may approve. The request comes back 403, the audit log gains a refused row against that actor, and the incident stays at awaiting-approval. **Let the approval window expire** With an incident awaiting approval, force the approval to expire. The status changes to escalated, the approve and reject buttons are disabled, and no action runs. #eng-oncall gets a message naming the escalation target from the recorded catalog and saying the action was not executed. The audit log records the same handoff. **Have the Glean agent propose an action that is not registered** Fire the off-script agent alarm, PAY-2233. The recorded agent proposes rollback-production-now, which is not in the action registry. Where the approval card would be, the dashboard says no action was offered and explains that the planner named an unregistered action. There are no approve or reject buttons, and the audit log records the refusal with glean-agent as the actor. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:oncall-copilot ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/oncall-copilot) Runs the recipe through the Glean cookbook plugin. [Agents](/cookbook.md#agents) collection 1. 1[Triage one support issue with a Glean Agent](/cookbook/triage-support-issue.md)Beginner 2. 2[Approve an agent action from a CLI](/cookbook/human-in-the-loop-agent.md)Beginner 3. 3On-call CopilotAdvanced At a glance CapabilitiesSearch, Chat, Agents, Tools, Workflows SurfacesPlatform API, Agents, Tools StatusShowcase Time\~1.5 hr Required scopes SEARCHCHATAGENTS --- [Complete apps](/cookbook.md#complete-apps) [ · ](/cookbook.md#complete-apps) [4](/cookbook.md#complete-apps) [ of](/cookbook.md#complete-apps) [ ](/cookbook.md#complete-apps) [5](/cookbook.md#complete-apps) # Answer an RFP or security questionnaire A review app that drafts cited answers to an RFP or security questionnaire, leaves unsupported questions blank, and requires a person to approve each answer. It starts with recorded Chat responses, so you can inspect its refusal paths before connecting it to Glean. \~45 minIntermediate ![RFP Responder review screen with cited draft answers and approval controls](/img/cookbook/previews/rfp-responder/preview.webp)View preview Problem A security questionnaire arrives with 200 rows. The answers exist in last quarter's SOC 2 report, a policy document, or the response you sent another customer. Someone spends a week finding them again. Drafting is not the dangerous part. A fluent answer with no supporting evidence can reach a customer's document over your signature. This app makes that failure visible. Questions with approved evidence get cited drafts. Questions with weak evidence are flagged for verification. Questions with no evidence stay blank and go to a subject matter expert. A person approves each answer before export. The default walkthrough uses recorded responses, so you can watch each path without credentials. Live mode runs as the signed-in person and limits customer-facing drafts to reviewed source URL prefixes. Architecture Questionnairexlsx / docx / CSV Parse + dedupexact merges only Platform Chatone call per question Gleanpermission-aware index Evidence checktopicality + approval Review + approvehuman gate, audit log Prerequisites Node 20 or newer Nothing else. The walkthrough replays recorded Chat responses, so it needs no credentials and makes no network calls Only for a live run: a Glean instance with company content indexed, a work email for sign-in or a Glean API token with the CHAT scope, and a reviewed list of document URL prefixes that may support customer-facing answers Steps 1 **Copy the project onto your machine** Creates an rfp-responder folder containing the local server, review app, sample questionnaire, and recorded Chat responses. Stay in the same parent directory for the remaining commands. ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/rfp-responder rfp-responder ``` 2 **Install dependencies** Installs the packages for the TypeScript server. Everything runs on your machine, and nothing is deployed. ``` cd rfp-responder && npm install ``` 3 **See what it refuses before connecting anything** Replays the recorded responses through the full questionnaire and checks that every draft has a citation, unsupported rows stay blank, weak evidence is flagged, and export stays behind approval. The command needs no credentials and makes no network calls. ``` npm run verify:fixture ``` 4 **Open the review app** Starts the local server and prints a Local URL. Open that URL in a browser. The app uses the same recorded responses, so there is nothing to sign in to. ``` npm start ``` 5 **Load the sample and check the refusals** Click Try the bundled sample, then Confirm and draft answers. The app parses 20 rows and merges the exact SSO duplicate before drafting 19 questions. SEC-01 and SEC-02 get cited answers. ACC-02 and ACC-03 are marked weak. ACC-04 and CMP-01 stay blank and need a subject matter expert. Merge only exact duplicates Similarity may rank possible duplicates for review, but must never merge security controls automatically. Topicality is not approval A source must be both relevant and explicitly approved for customer-facing use before it can support a draft. Single-user permission boundary The app runs as the credential owner. Multi-user deployment requires per-user authentication and authorization. Take it further * Add an xlsx and docx reader so real questionnaires can be uploaded directly. The parser here reads CSV to keep the evidence and approval logic legible. * Write answers back into the source document with a custom tool instead of exporting, keeping the confirm step and the approval log. * Give the answer library its own access control before sharing it across a team. It's a cache of retrieved content, so it can leak across the permission boundary the rest of the app respects. * Route "needs SME" rows to their owners automatically and track which ones came back. The assignment field here does not send a message. Try it **Draft the bundled security questionnaire** Click Try the bundled sample, then Confirm and draft answers. The app parses 20 rows across 4 tabs, merges 1 exact duplicate, and asks 19 questions. The review summary shows 10 strongly grounded answers, 2 weak answers to verify, and 8 questions that need a subject matter expert. SEC-01 and SEC-02 have cited drafts. **Check what happens when the evidence does not support an answer** After drafting the bundled sample, ACC-04 and CMP-01 show no draft answer and say that the question needs a subject matter expert. The rows have no citations and offer assignment to a subject matter expert. **Review an answer backed by weak evidence** After drafting the bundled sample, ACC-02 and ACC-03 have draft answers but appear in the weak evidence bucket. Their evidence notes explain that a person must verify the source before accepting the answer. **Compare a live questionnaire run with a colleague who has narrower access** In separate live runs, each person signs in as themselves and uploads the same questionnaire. A row backed only by a document the second person cannot access stays blank and needs a subject matter expert. The app does not impersonate either person. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:rfp-responder ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/rfp-responder) Runs the recipe through the Glean cookbook plugin. [Complete apps](/cookbook.md#complete-apps) collection 1. 1[Build an IT helpdesk page in Lovable](/cookbook/no-code-it-helpdesk-lovable.md)Beginner 2. 2[Build a PTO lookup page in Replit](/cookbook/no-code-pto-lookup-replit.md)Beginner 3. 3[Onboarding Hub: a day-one checklist grounded in your own docs](/cookbook/onboarding-hub.md)Intermediate 4. 4Answer an RFP or security questionnaireIntermediate 5. 5[Customer 360: an account page built from your own content](/cookbook/customer-360.md)Intermediate At a glance CapabilitiesChat, Workflows SurfacesPlatform API StatusShowcase Time\~45 min Required scopes CHAT --- [Search](/cookbook.md#search) [ · ](/cookbook.md#search) [1](/cookbook.md#search) [ of](/cookbook.md#search) [ ](/cookbook.md#search) [1](/cookbook.md#search) # Search Glean with discovered filters Use the official TypeScript API client to search across all of your Glean content by default, or discover datasources and common filter fields before applying an explicit selection to permission-aware Platform Search. \~15 minBeginner Problem Search filters differ across Glean instances and connected sources. Hard-coding a datasource or field from another environment makes a first integration brittle, while hand-writing HTTP hides the supported client developers should use. This quickstart uses the official TypeScript API client to discover what the signed-in user can filter, then carries that returned selection into a typed Search request that matches the reader's own instance and permissions. Code walkthrough The runnable scaffold uses the published Glean auth package for tenant discovery and refreshable OAuth credentials, gives that token provider to the official API client, and keeps the Search sequence explicit. ### Configure the API client Resolve the backend from work email or an explicit server URL, then give the Glean client the auth package's async token provider, a finite timeout per attempt, and bounded exponential backoff. A user-scoped SEARCH token remains an explicit fallback. src/client.ts ``` import { Glean, type SDKOptions } from '@gleanwork/api-client'; import { createGleanTokenProvider, discoverGleanTenant } from '@gleanwork/auth'; const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']); interface GleanClientTarget { email?: string; serverUrl?: string; } async function resolveServerUrl({ email, serverUrl }: GleanClientTarget) { const explicit = serverUrl?.trim(); if (explicit) return explicit; const workEmail = email?.trim(); if (workEmail) return (await discoverGleanTenant(workEmail)).serverUrl; const configured = process.env.GLEAN_SERVER_URL?.trim(); if (configured) return configured; throw new Error( 'Pass --email or --server-url, or set GLEAN_SERVER_URL in your environment.', ); } export async function createGleanClient(target: GleanClientTarget) { const serverURL = await resolveServerUrl(target); const server = new URL(serverURL); const loopback = LOOPBACK_HOSTS.has(server.hostname); if ( (server.protocol !== 'https:' && !loopback) || server.username || server.password || server.search || server.hash || (server.pathname && server.pathname !== '/') || (!loopback && server.port) ) { throw new Error('Use a complete Glean backend HTTPS origin.'); } const staticToken = process.env.GLEAN_API_TOKEN?.trim(); const options = { serverURL: server.origin, apiToken: staticToken || createGleanTokenProvider({ serverUrl: server.origin, scopes: ['search'], }), timeoutMs: 30_000, retryConfig: { strategy: 'backoff', backoff: { initialInterval: 500, maxInterval: 5_000, exponent: 2, maxElapsedTime: 90_000, }, retryConnectionErrors: true, }, } satisfies SDKOptions; return new Glean(options); } ``` ### Discover filters, then search Search broadly by default, or list visible fields and request query-specific suggestions when you explicitly narrow the query to one datasource. src/search.ts ``` import { chooseDatasources, chooseFilter, createTerminal, parseCliOptions, printSearchResponse, } from './cli.js'; import { createGleanClient } from './client.js'; import type { PlatformFilter } from '@gleanwork/api-client/models/components'; import { formatSdkError } from './errors.js'; /** * Searches across all datasources by default. When a datasource list is * supplied, discovers query-specific values for a single datasource and * applies the selected filter. * * @remarks * Filter discovery returns a best-effort catalog and may omit valid fields. * Query-specific values are bounded suggestions, not guaranteed matches. The * final Search request still enforces the caller's permissions. */ async function main() { const cliOptions = parseCliOptions(); const glean = await createGleanClient({ email: cliOptions.email, serverUrl: cliOptions.serverUrl, }); const terminal = createTerminal(); try { let datasources: string[] | undefined; let filter: PlatformFilter | undefined = cliOptions.filter; if (cliOptions.datasources || cliOptions.autoSelect || filter) { const { result: catalog } = await glean.search.listFilters(); datasources = await chooseDatasources( catalog.datasources, cliOptions.datasources, cliOptions.autoSelect, terminal, ); if (!filter && datasources.length === 1) { const datasource = datasources[0]; const { result: suggestions } = await glean.search.listFilters( datasources, cliOptions.query, ); const filterInfo = suggestions.datasources.find( (candidate) => candidate.datasource === datasource, ); if (!filterInfo) { throw new Error( `No filter metadata for "${datasource}" (request ${suggestions.request_id}).`, ); } filter = await chooseFilter( filterInfo, undefined, cliOptions.autoSelect, terminal, ); } } const searchRequest = { query: cliOptions.query, page_size: 10, ...(datasources ? { datasources } : {}), ...(filter ? { filters: [filter] } : {}), }; let cursor: string | undefined; let resultOffset = 0; for (let page = 1; page <= cliOptions.pages; page += 1) { const searchResponse = await glean.search.query({ ...searchRequest, ...(cursor ? { cursor } : {}), }); printSearchResponse( searchResponse, datasources, filter, page, resultOffset, cliOptions.pages, ); resultOffset += searchResponse.results.length; if (!searchResponse.has_more) break; if (!searchResponse.next_cursor) { throw new Error('Search reported has_more without a next_cursor.'); } if (searchResponse.next_cursor === cursor) { throw new Error( 'Search returned the same cursor for consecutive pages.', ); } cursor = searchResponse.next_cursor; } } finally { terminal?.close(); } } main().catch((error: unknown) => { console.error(formatSdkError(error)); process.exitCode = 1; }); ``` Architecture Your CLIquery and filter choice TypeScript API clienttyped requests and responses Search Filtersvisible datasources and fields Platform Searchpermission-aware results Prerequisites Node 22.12.0 or newer A Glean instance with content indexed Your work email, or the complete Glean backend origin shown under Server instance (QE) A tenant that permits this public OAuth client and search scope through DCR; an administrator-provisioned OAuth client or user-scoped SEARCH token is the fallback Run it yourself 1 **Scaffold the project** Copies the runnable TypeScript Search CLI and fixture tests into a new directory. OAuth login and secure token storage come from the pinned @gleanwork/auth package. ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/search-with-discovered-filters search-with-discovered-filters ``` 2 **Install dependencies** ``` cd search-with-discovered-filters && npm install ``` 3 **Run the fixture tests** Runs the Vitest fixture suite without credentials or network access, covering catalog discovery, query-backed suggestions, retries, typed errors, and field-filter propagation. ``` npm test ``` 4 **Sign in with OAuth** Discovers your Glean backend from work email, completes Authorization Code with PKCE for search and offline\_access, and stores refreshable credentials outside the project. Use --server-url for an explicit backend, GLEAN\_OAUTH\_CLIENT\_ID for an administrator-provisioned public client, or GLEAN\_API\_TOKEN as a user-scoped fallback. ``` npm run login -- --email "" ``` 5 **Verify against your instance** Searches your topic across all datasources and validates the Search response shape and pagination state. Add --pages 2 to fetch a second page, --datasources to narrow the query, or --auto-select to exercise the discovered datasource and suggested-filter path. ``` npm run verify -- --email "" --query "" ``` 6 **Discover filters and search** Runs the non-interactive CLI path, selecting the first returned datasource and suggested field value, then prints results, warnings, pagination state, and request IDs. Add --pages 2 to fetch the next result page. Omit --auto-select when running interactively to choose them yourself. ``` npm start -- --email "" --query "" --auto-select --pages 2 ``` Discovery is best effort A field omitted from Search Filters may still be valid, and suggested values are bounded and non-exhaustive. Reusing a returned value does not guarantee matching results. OAuth policy varies by tenant DCR may be disabled, restricted to approved applications or redirect URI patterns, or unable to grant search. Use an administrator-provisioned public OAuth client when available; a user-scoped SEARCH token is the tutorial fallback. Take it further * Use --pages to follow next\_cursor through a bounded number of Search requests without treating the cursor as data or constructing it yourself. * Persist the catalog briefly for UI rendering, but refresh it and continue to treat it as best effort rather than an exhaustive schema. * Build a faceted search interface that requests query-specific values after the user chooses one datasource. * Add time\_range alongside discovered filters for bounded recency searches. Try it **Search for a topic you know exists in your Glean instance** Searches across all datasources by default and returns a valid permission-aware Search response without assuming a fixed result count. Explicit --datasources or --auto-select enables datasource and suggested-filter discovery. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:search-with-discovered-filters ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/search-with-discovered-filters) Runs the recipe through the Glean cookbook plugin. [Search](/cookbook.md#search) collection 1. 1Search Glean with discovered filtersBeginner Auth Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (search). At a glance CapabilitiesSearch SurfacesPlatform API StatusQuickstart Time\~15 min Required scopes SEARCH --- [Skills](/cookbook.md#skills) [ · ](/cookbook.md#skills) [3](/cookbook.md#skills) [ of](/cookbook.md#skills) [ ](/cookbook.md#skills) [3](/cookbook.md#skills) # Publish a versioned skill bundle After a first persist, prove name-based version supersession, retrieve a specific version, stage downloaded bytes in a bounded zip sandbox, and delete only the run-owned skill. \~20 minIntermediate Problem After a first Skills persist, teams still need to prove a later publish becomes a version rather than a duplicate, retrieve that exact version, and inspect the stored zip without executing it. This recipe is that versioning job. The Beginner validate-and-publish-skill quickstart owns the first persist. Code walkthrough The runnable scaffold calls the Skills API through the official SDK, keeps validation separate from persistence, verifies version supersession directly, and treats downloaded archives as untrusted. ### Configure the Skills API client Resolve the reader's tenant and use the native Skills scopes or the recorded legacy compatibility mode. src/client.ts ``` import fs from 'node:fs/promises'; import path from 'node:path'; import { Glean, type SDKOptions } from '@gleanwork/api-client'; import { createGleanTokenProvider, discoverGleanTenant } from '@gleanwork/auth'; const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']); const SCOPE_MODE_FILE = '.glean-scope-mode'; export interface GleanClientTarget { email?: string; serverUrl?: string; } async function loadDotEnv() { let text: string; try { text = await fs.readFile(path.join(process.cwd(), '.env'), 'utf8'); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return; throw error; } for (const raw of text.split(/\r?\n/)) { const line = raw.trim(); if (!line || line.startsWith('#')) continue; const eq = line.indexOf('='); if (eq <= 0) continue; const key = line.slice(0, eq).trim(); let value = line.slice(eq + 1).trim(); if ( (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")) ) { value = value.slice(1, -1); } if (process.env[key] === undefined) process.env[key] = value; } } async function resolveServerUrl({ email, serverUrl }: GleanClientTarget) { const explicit = serverUrl?.trim(); if (explicit) return explicit; const workEmail = email?.trim(); if (workEmail) return (await discoverGleanTenant(workEmail)).serverUrl; const configured = process.env.GLEAN_SERVER_URL?.trim(); if (configured) return configured; throw new Error( 'Pass --email or --server-url, or set GLEAN_SERVER_URL in your environment.', ); } async function configuredScopes(log: (message: string) => void) { const envMode = process.env.GLEAN_SKILLS_SCOPE_MODE?.trim(); let fileMode: string | undefined; try { fileMode = ( await fs.readFile(path.join(process.cwd(), SCOPE_MODE_FILE), 'utf8') ).trim(); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; } if (envMode && fileMode && envMode !== fileMode) { log( `GLEAN_SKILLS_SCOPE_MODE=${envMode} overrides .glean-scope-mode (${fileMode}).`, ); } else if (envMode) { log(`Using GLEAN_SKILLS_SCOPE_MODE=${envMode}.`); } else if (fileMode) { log(`Using .glean-scope-mode (${fileMode}).`); } else { log('Using native skills:read and skills:write scopes.'); } const mode = envMode || fileMode; return mode === 'legacy' ? ['SKILLS'] : ['skills:read', 'skills:write']; } export async function createGleanClient( target: GleanClientTarget, log: (message: string) => void = () => undefined, ) { await loadDotEnv(); const serverURL = await resolveServerUrl(target); const server = new URL(serverURL); const loopback = LOOPBACK_HOSTS.has(server.hostname); if ( (server.protocol !== 'https:' && !loopback) || server.username || server.password || server.search || server.hash || (server.pathname && server.pathname !== '/') || (!loopback && server.port) ) { throw new Error('Use a complete Glean backend HTTPS origin.'); } const staticToken = process.env.GLEAN_API_TOKEN?.trim(); let apiToken: string | ReturnType; if (staticToken) { log('Using GLEAN_API_TOKEN from the environment.'); apiToken = staticToken; } else { const scopes = await configuredScopes(log); log(`Using the OAuth session (${scopes.join(', ')}).`); apiToken = createGleanTokenProvider({ serverUrl: server.origin, scopes, }); } const options = { serverURL: server.origin, apiToken, timeoutMs: 30_000, retryConfig: { strategy: 'backoff', backoff: { initialInterval: 500, maxInterval: 5_000, exponent: 2, maxElapsedTime: 90_000, }, retryConnectionErrors: true, }, } satisfies SDKOptions; return new Glean(options); } ``` ### Prove supersession and inspect a version Publish the same unique name twice, confirm the skill ID is stable while the version advances, then retrieve that version's metadata and content directly. src/workflow\.ts ``` import fs from 'node:fs/promises'; import path from 'node:path'; import { randomBytes } from 'node:crypto'; import type { Glean } from '@gleanwork/api-client'; import { CleanupFailedError } from './errors.js'; import { readBundle, readStream, stageDownloadedBundle } from './bundle.js'; export type SkillsApi = Pick< Glean['skills'], | 'create' | 'delete' | 'list' | 'listVersions' | 'retrieve' | 'retrieveContent' | 'retrieveVersion' | 'retrieveVersionContent' | 'validate' >; export interface PublishResult { id: string; displayName: string; version: number; minorVersion: number; } function rethrow(error: unknown): never { throw error instanceof Error ? error : new Error('Verification failed.'); } export function cleanupCommand( skillId: string, auth: { email?: string; serverUrl?: string } = {}, ) { const parts = [`npm start -- cleanup --id ${skillId} --yes`]; if (auth.serverUrl?.trim()) { parts.push(`--server-url ${auth.serverUrl.trim()}`); } else if (auth.email?.trim()) { parts.push(`--email ${auth.email.trim()}`); } else { parts.push('--email '); } return parts.join(' '); } export function publishedSkillLine(result: PublishResult) { return `Published ${result.displayName} (${result.id}) at version ${result.version}.${result.minorVersion}.`; } export function verifiedSuccessLine(result: PublishResult) { return `Verified ${result.displayName} (${result.id}) at version ${result.version}.${result.minorVersion}; cleanup completed.`; } export function stagingDestination( stageDir: string, id: string, version: number, minorVersion: number, ) { return path.resolve(stageDir, id, `v${version}.${minorVersion}`); } export async function findSkillByName( api: SkillsApi, displayName: string, ): Promise { let cursor: string | undefined; do { const page = await api.list(100, cursor); const match = page.skills.find( (skill) => skill.display_name === displayName, ); if (match) return match.id; cursor = page.next_cursor ?? undefined; } while (cursor); return undefined; } export async function deleteCapturedIds( api: SkillsApi, ids: string[], log: (message: string) => void, ) { const remaining: string[] = []; for (const id of ids) { log(`Deleting run-owned skill ${id}...`); try { await api.delete(id); } catch { remaining.push(id); } } return remaining; } export async function publishBundle( api: SkillsApi, bundlePath: string, ): Promise { const bundle = await readBundle(bundlePath); const validation = await api.validate({ file: bundle }); const created = await api.create({ file: bundle }); if (created.skill.display_name !== validation.metadata.display_name) { throw new Error('Published skill name does not match validated metadata.'); } return { id: created.skill.id, displayName: created.skill.display_name, version: created.skill.latest_version, minorVersion: created.skill.latest_minor_version, }; } export async function publishAndStage( api: SkillsApi, options: { bundlePath: string; stageDir: string; log: (message: string) => void; }, ) { const result = await publishBundle(api, options.bundlePath); options.log(publishedSkillLine(result)); const destination = stagingDestination( options.stageDir, result.id, result.version, result.minorVersion, ); const response = await api.retrieveContent(result.id); const files = await stageDownloadedBundle( await readStream(response.result), destination, ); return { result, files, destination }; } function manifest(displayName: string, description: string) { return `---\nname: ${displayName}\ndescription: ${description}\n---\n\n# Publishing verification\n\nThis fixture verifies the Skills publishing lifecycle. It contains no executable code.\n`; } function versionAdvanced( first: { latest_version: number; latest_minor_version: number }, second: { latest_version: number; latest_minor_version: number }, ) { return ( second.latest_version > first.latest_version || (second.latest_version === first.latest_version && second.latest_minor_version > first.latest_minor_version) ); } async function assertManifest( api: SkillsApi, skillId: string, expectedName: string, destination: string, version?: number, ) { const response = version === undefined ? await api.retrieveContent(skillId) : await api.retrieveVersionContent(skillId, version); const archive = await readStream(response.result); const files = await stageDownloadedBundle(archive, destination); const manifestPath = files.find( (file) => path.posix.basename(file) === 'SKILL.md', ); if (!manifestPath) throw new Error('Staged bundle has no SKILL.md.'); const content = await fs.readFile( path.join(destination, manifestPath), 'utf8', ); if (!content.includes(`name: ${expectedName}`)) { throw new Error('Downloaded SKILL.md does not match the published skill.'); } } export async function verifyPublishingLifecycle( api: SkillsApi, options: { workDir: string; cleanup: boolean; auth?: { email?: string; serverUrl?: string }; log?: (message: string) => void; }, ): Promise { const log = options.log ?? (() => undefined); const uniqueName = `cookbook-publish-${randomBytes(8).toString('hex')}`; const runRoot = path.join(options.workDir, uniqueName); const firstPath = path.join(runRoot, 'v1', 'SKILL.md'); const secondPath = path.join(runRoot, 'v2', 'SKILL.md'); let createdId: string | undefined; let result: PublishResult | undefined; let workError: unknown; await fs.mkdir(path.dirname(firstPath), { recursive: true }); await fs.mkdir(path.dirname(secondPath), { recursive: true }); await fs.writeFile( firstPath, manifest(uniqueName, 'Cookbook publishing verification version one.'), { flag: 'wx', mode: 0o600 }, ); await fs.writeFile( secondPath, manifest(uniqueName, 'Cookbook publishing verification version two.'), { flag: 'wx', mode: 0o600 }, ); try { log('Validating the first bundle without persisting it...'); const firstBundle = await readBundle(firstPath); const validation = await api.validate({ file: firstBundle }); if (validation.metadata.display_name !== uniqueName) { throw new Error('Validation returned an unexpected skill name.'); } log('Confirming invalid frontmatter is rejected without a create call...'); const invalidPath = path.join(runRoot, 'invalid', 'SKILL.md'); await fs.mkdir(path.dirname(invalidPath), { recursive: true }); await fs.writeFile(invalidPath, '# Missing frontmatter\n', { flag: 'wx', mode: 0o600, }); let rejected = false; try { await api.validate({ file: await readBundle(invalidPath) }); } catch { rejected = true; } if (!rejected) throw new Error('Invalid SKILL.md unexpectedly validated.'); log('Publishing the first version of a uniquely named skill...'); const first = await api.create({ file: firstBundle }); createdId = first.skill.id; const retrieved = await api.retrieve(createdId); if (retrieved.skill.id !== createdId) { throw new Error('Direct retrieval returned a different skill.'); } await assertManifest( api, createdId, uniqueName, path.join(runRoot, 'staged-latest-v1'), ); log('Publishing the same name again to create a newer version...'); const second = await api.create({ file: await readBundle(secondPath) }); if (second.skill.id !== createdId) { throw new Error('Name-based supersession created a different skill ID.'); } if (!versionAdvanced(first.skill, second.skill)) { throw new Error('The second publish did not advance the skill version.'); } const versions = await api.listVersions(createdId, 100); const latest = versions.versions.find( (version) => version.version === second.skill.latest_version && version.is_latest, ); if (!latest) throw new Error('The new version is missing from listVersions.'); const version = await api.retrieveVersion( createdId, second.skill.latest_version, ); if (version.version.skill_id !== createdId || !version.version.is_latest) { throw new Error( 'Direct version retrieval did not return the latest version.', ); } await assertManifest( api, createdId, uniqueName, path.join(runRoot, 'staged-version-v2'), second.skill.latest_version, ); result = { id: createdId, displayName: second.skill.display_name, version: second.skill.latest_version, minorVersion: second.skill.latest_minor_version, }; } catch (error) { workError = error; } const remaining = createdId && options.cleanup ? await deleteCapturedIds(api, [createdId], log) : []; await fs.rm(runRoot, { recursive: true, force: true }); if (remaining.length > 0) { throw new CleanupFailedError( remaining, remaining.map((id) => cleanupCommand(id, options.auth)).join('\n '), workError, ); } if (workError) rethrow(workError); if (!result) throw new Error('Verification did not produce a result.'); return result; } ``` ### Stage downloaded bundles safely Extract into a fresh sandbox while rejecting traversal, links, special files, overwrites, and configured size limits. Retrieved content is never executed. src/bundle.ts ``` import fs from 'node:fs/promises'; import path from 'node:path'; import yauzl, { type Entry, type ZipFile } from 'yauzl'; export interface BundleLimits { maxEntries: number; maxFileBytes: number; maxTotalBytes: number; } export const DEFAULT_LIMITS: BundleLimits = { maxEntries: 100, maxFileBytes: 2 * 1024 * 1024, maxTotalBytes: 10 * 1024 * 1024, }; export async function readBundle(filePath: string) { const resolved = path.resolve(filePath); const stats = await fs.stat(resolved); if (!stats.isFile()) { throw new Error('Bundle must be a SKILL.md, .zip, or .skill file.'); } const fileName = path.basename(resolved); if ( fileName !== 'SKILL.md' && !fileName.endsWith('.zip') && !fileName.endsWith('.skill') ) { throw new Error('Bundle must be named SKILL.md or end in .zip or .skill.'); } return { fileName, content: new Uint8Array(await fs.readFile(resolved)) }; } export async function readStream( stream: ReadableStream, maxBytes = DEFAULT_LIMITS.maxTotalBytes, ): Promise { const reader = stream.getReader(); const chunks: Uint8Array[] = []; let total = 0; while (true) { const { done, value } = await reader.read(); if (done) break; total += value.byteLength; if (total > maxBytes) { await reader.cancel(); throw new Error(`Downloaded bundle exceeds ${maxBytes} bytes.`); } chunks.push(value); } return Buffer.concat(chunks); } function openZip(buffer: Buffer): Promise { return new Promise((resolve, reject) => { yauzl.fromBuffer(buffer, { lazyEntries: true }, (error, zipFile) => { if (error) reject(error); else if (!zipFile) reject(new Error('Could not open downloaded bundle.')); else resolve(zipFile); }); }); } function validateEntry(entry: Entry): { relativePath: string; directory: boolean; } { const relativePath = entry.fileName; if ( !relativePath || relativePath.includes('\\') || relativePath.includes('\0') || path.posix.isAbsolute(relativePath) || path.posix.normalize(relativePath) !== relativePath || relativePath.split('/').includes('..') ) { throw new Error(`Unsafe bundle path: ${JSON.stringify(relativePath)}`); } if ((entry.generalPurposeBitFlag & 0x1) !== 0) { throw new Error( `Encrypted bundle entries are not supported: ${relativePath}`, ); } const unixMode = entry.externalFileAttributes >>> 16; const fileType = unixMode & 0o170000; if (fileType === 0o120000) { throw new Error(`Symbolic links are not allowed: ${relativePath}`); } const directory = relativePath.endsWith('/'); if ( fileType !== 0 && fileType !== 0o100000 && !(directory && fileType === 0o040000) ) { throw new Error( `Only regular files and directories are allowed: ${relativePath}`, ); } return { relativePath, directory }; } function readEntry( zipFile: ZipFile, entry: Entry, maxBytes: number, ): Promise { return new Promise((resolve, reject) => { zipFile.openReadStream(entry, (error, stream) => { if (error) { reject(error); return; } if (!stream) { reject(new Error(`Could not read ${entry.fileName}.`)); return; } const chunks: Buffer[] = []; let total = 0; let settled = false; stream.on('data', (chunk: Buffer) => { total += chunk.byteLength; if (total > maxBytes) { settled = true; stream.destroy(); reject( new Error(`${entry.fileName} exceeds the per-file size limit.`), ); return; } chunks.push(chunk); }); stream.once('error', (error) => { if (!settled) reject(error); }); stream.once('end', () => { if (!settled) resolve(Buffer.concat(chunks)); }); }); }); } export async function stageDownloadedBundle( archive: Buffer, destination: string, limits: BundleLimits = DEFAULT_LIMITS, ): Promise { const root = path.resolve(destination); await fs.mkdir(path.dirname(root), { recursive: true }); await fs.mkdir(root, { recursive: false, mode: 0o700 }); const zipFile = await openZip(archive); const staged: string[] = []; let entries = 0; let totalBytes = 0; try { await new Promise((resolve, reject) => { const fail = (error: unknown) => { zipFile.close(); reject(error instanceof Error ? error : new Error(String(error))); }; zipFile.once('error', fail); zipFile.once('end', resolve); zipFile.on('entry', (entry: Entry) => { void (async () => { entries += 1; if (entries > limits.maxEntries) { throw new Error( `Bundle contains more than ${limits.maxEntries} entries.`, ); } const { relativePath, directory } = validateEntry(entry); const target = path.resolve(root, relativePath); if (target !== root && !target.startsWith(`${root}${path.sep}`)) { throw new Error(`Bundle path escapes the sandbox: ${relativePath}`); } if (directory) { await fs.mkdir(target, { recursive: true, mode: 0o700 }); } else { if (entry.uncompressedSize > limits.maxFileBytes) { throw new Error( `${relativePath} exceeds the per-file size limit.`, ); } const content = await readEntry( zipFile, entry, limits.maxFileBytes, ); if (content.byteLength > limits.maxFileBytes) { throw new Error( `${relativePath} exceeds the per-file size limit.`, ); } totalBytes += content.byteLength; if (totalBytes > limits.maxTotalBytes) { throw new Error('Bundle exceeds the aggregate size limit.'); } await fs.mkdir(path.dirname(target), { recursive: true, mode: 0o700, }); await fs.writeFile(target, content, { flag: 'wx', mode: 0o600 }); staged.push(relativePath); } zipFile.readEntry(); })().catch(fail); }); zipFile.readEntry(); }); } catch (error) { await fs.rm(root, { recursive: true, force: true }); throw error; } finally { zipFile.close(); } if (!staged.some((file) => path.posix.basename(file) === 'SKILL.md')) { await fs.rm(root, { recursive: true, force: true }); throw new Error('Downloaded bundle does not contain SKILL.md.'); } return staged; } ``` Architecture Your skill bundleSKILL.md, .zip, or .skill Validationmetadata and normalized layout Versioned Skills APIstable ID, advancing version Fresh sandboxbounded zip extraction Prerequisites Node.js 22.12.0 or newer A Glean instance with Skills enabled Your work email, or the complete Glean backend HTTPS origin A tenant that permits the native skills:read and skills:write OAuth scopes; the legacy SKILLS compatibility scope or a user-scoped token is the fallback The scaffold includes a sample SKILL.md at fixtures/sample-skill/SKILL.md; the later publish step can use that sample or your own bundle Run it yourself 1 **Copy the project onto your machine** Copy the Skills versioning CLI, sample bundle, CI example, and credential-free fixture tests into a new directory. OAuth login and secure token storage come from the pinned @gleanwork/auth package. ``` npx -y tiged@2.12.8 gleanwork/glean-cookbook/recipes/skill-publishing-pipeline skill-publishing-pipeline ``` 2 **Install dependencies** ``` cd skill-publishing-pipeline && npm install ``` 3 **Run the fixture tests** Run name-based supersession, version retrieval, scope fallback classification, and adversarial archive tests without credentials or network access. ``` npm test ``` 4 **Sign in with OAuth** Discover your Glean backend from work email and request skills:read and skills:write. Only a recognized scope-grant failure triggers one retry with legacy SKILLS. If OAuth is not available, skip this command: copy .env.example to .env and fill GLEAN\_API\_TOKEN and GLEAN\_SERVER\_URL. ``` npm run login -- --email "" ``` 5 **Pass an explicit backend if you need one** If email discovery is wrong, pass --server-url with the complete Glean backend HTTPS origin on login, verify, and start. If DCR is restricted, export GLEAN\_OAUTH\_CLIENT\_ID in your shell before npm run login. npm run login does not read .env, so do not store that client id only in .env. 6 **Verify versioning on your instance** Create a cryptographically unique sample, publish a second version under the same name, retrieve and stage that version's content in a bounded sandbox, then permanently delete only the ID returned by this run. Success prints a Verified line that ends with cleanup completed. ``` npm run verify -- --email "" ``` 7 **Publish a new version of your bundle** Validate fixtures/sample-skill/SKILL.md by default, confirm before name-based supersession if that display name already exists, and stage the returned zip under staged/ID/vVERSION.MINOR so a second publish does not overwrite the first. Pass --bundle with your own SKILL.md, .zip, or .skill, and --stage-dir to choose a different parent folder. The command prints the exact ID before staging, which you need for optional cleanup. ``` npm start -- publish --email "" ``` 8 **Push this directory as a GitHub repository** Treat this scaffold directory as the GitHub repository root. The workflow at .github/workflows/publish-skill.yml is already in the scaffold and already points at fixtures/sample-skill/SKILL.md. Push this directory, then set a user-scoped token secret and backend-origin variable in that repository. 9 **Delete the published skill only when you intend to** Deletion permanently removes every version. Pass only the exact ID printed by your publish run and read the confirmation prompt. ``` npm start -- cleanup --id "PASTE_THE_ID_PRINTED_BY_PUBLISH" --email "" ``` Validate before persistence Call validate first and stop on any error. Validation itself never creates or updates a skill. Confirm supersession If the validated display name already exists, show its ID and require confirmation before create adds a version. Treat content as untrusted Stage downloaded bundles in a fresh bounded sandbox, never overwrite files, and never execute bundle contents. Delete by captured ID only Deletion removes every version. Never choose a cleanup target by display name or a broad catalog search. If delete fails, do not report cleanup completed. Publishing is not execution The API stores and distributes skill bundles. This recipe does not run the skill or prove how an agent host will interpret it. OAuth policy varies by tenant Native Skills scopes may not yet be grantable everywhere. The login wrapper uses the legacy compatibility scope only for a recognized scope-grant failure; a user-scoped token remains an explicit fallback. Take it further * Use glean.skills.createVersion() when your system already tracks a specific skill ID and explicit version creation is clearer than name-based supersession. * PATCH a captured skill ID to enable or disable it without changing stored content. * Import and sync from GitHub in a later Skills recipe instead of folding that source of truth into this pipeline. Try it **Publish a uniquely named sample skill twice, inspect the new version, then clean up the run-owned skill** Stdout ends with Verified \ (\) at version \.\; cleanup completed. If delete fails, the process exits non-zero, prints the remaining ID and a cleanup command that includes --id, --yes, and the same --email or --server-url used for the run, and does not print that success line. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:skill-publishing-pipeline ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/skill-publishing-pipeline) Runs the recipe through the Glean cookbook plugin. [Skills](/cookbook.md#skills) collection 1. 1[Validate skill publishing](/cookbook/validate-and-publish-skill.md)Beginner 2. 2[Import a skill from GitHub](/cookbook/import-skill-from-github.md)Intermediate 3. 3Publish a versioned skill bundleIntermediate Auth Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (skills:read, skills:write). At a glance CapabilitiesSkills SurfacesPlatform API StatusProduction pattern Time\~20 min Required scopes skills:readskills:write --- [Chat](/cookbook.md#chat) [ · ](/cookbook.md#chat) [1](/cookbook.md#chat) [ of](/cookbook.md#chat) [ ](/cookbook.md#chat) [1](/cookbook.md#chat) # Stream a cited Chat response Use the modern Platform Chat API to send a permission-aware question, continue the conversation, and read server-sent events with the official TypeScript API client's `createStream` `EventStream`. \~20 minBeginner Problem A first Chat integration needs more than a request and a string: it must preserve the user's permissions, continue a conversation, and consume streamed events correctly. This quickstart shows the modern Platform Chat API with the official TypeScript client's `createStream` `EventStream`. Code walkthrough The runnable scaffold uses `glean.chat.createStream`, the published Glean auth package, and the official API client. Every turn iterates a typed `EventStream`. ### Configure the Platform API client Resolve the backend from work email or an explicit server URL, then construct `Glean` with a refreshable OAuth token provider or `GLEAN_API_TOKEN`. src/client.ts ``` import { Glean, type SDKOptions } from '@gleanwork/api-client'; import { createGleanTokenProvider, discoverGleanTenant } from '@gleanwork/auth'; const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']); export interface GleanClientTarget { email?: string; serverUrl?: string; } async function resolveServerUrl({ email, serverUrl }: GleanClientTarget) { const explicit = serverUrl?.trim(); if (explicit) return explicit; const workEmail = email?.trim(); if (workEmail) return (await discoverGleanTenant(workEmail)).serverUrl; const configured = process.env.GLEAN_SERVER_URL?.trim(); if (configured) return configured; throw new Error( 'Pass --email or --server-url, or set GLEAN_SERVER_URL in your environment.', ); } export async function createGleanClient(target: GleanClientTarget) { const serverURL = await resolveServerUrl(target); const server = new URL(serverURL); const loopback = LOOPBACK_HOSTS.has(server.hostname); if ( (server.protocol !== 'https:' && !loopback) || server.username || server.password || server.search || server.hash || (server.pathname && server.pathname !== '/') || (!loopback && server.port) ) { throw new Error('Use a complete Glean backend HTTPS origin.'); } const staticToken = process.env.GLEAN_API_TOKEN?.trim(); const options = { serverURL: server.origin, apiToken: staticToken || createGleanTokenProvider({ serverUrl: server.origin, scopes: ['chat'], }), } satisfies SDKOptions; return new Glean(options); } ``` ### Iterate `createStream` events Call `glean.chat.createStream` and for-await the typed `EventStream`. Write each `RESPONSE_OUTPUT_TEXT_DELTA.data.delta` immediately in raw mode, or accumulate the complete document for terminal rendering. Keep `RESPONSE_COMPLETED.data.response` for citations. src/stream.ts ``` import type { Glean } from '@gleanwork/api-client'; import type { PlatformChatCompletedResponse } from '@gleanwork/api-client/models/components'; export interface ChatTurn { completed?: PlatformChatCompletedResponse; conversationId?: string; } export async function streamTurn( client: Glean, input: string, conversationId?: string, onDelta?: (delta: string) => void, ): Promise { const stream = await client.chat.createStream({ conversation_id: conversationId, input, store: true, }); let completed: PlatformChatCompletedResponse | undefined; for await (const event of stream) { switch (event.event) { case 'RESPONSE_OUTPUT_TEXT_DELTA': onDelta?.(event.data.delta); break; case 'RESPONSE_COMPLETED': completed = event.data.response; break; case 'RESPONSE_FAILED': throw new Error(event.data.response.error.message); case 'RESPONSE_CREATED': case 'RESPONSE_PROGRESS': case 'RESPONSE_OUTPUT_TEXT_DONE': break; default: { const _exhaustive: never = event; void _exhaustive; } } } return { completed, conversationId: completed?.conversation_id ?? undefined, }; } ``` ### Continue a conversation and render citations Store the first turn, pass its `conversation_id` to a follow-up, and render citation sources and snippets from the completed response. src/chat.ts ``` import type { PlatformChatCompletedResponse } from '@gleanwork/api-client/models/components'; import { createGleanClient, type GleanClientTarget } from './client.js'; import { createMarkdownOutput, type OutputFormat } from './output.js'; import { streamTurn } from './stream.js'; interface OutputTarget { columns?: number; isTTY?: boolean; write(chunk: string): unknown; } export interface ChatOptions extends GleanClientTarget { followUp?: string; format: OutputFormat; prompt: string; } function printCitations( response: PlatformChatCompletedResponse, output: ReturnType, ) { const citations = response.output.flatMap((message) => message.content.flatMap((content) => content.annotations ?? []), ); if (citations.length === 0) return; let text = '\nSources:\n'; for (const [index, citation] of citations.entries()) { for (const source of citation.sources) { const title = 'title' in source && typeof source.title === 'string' ? source.title : undefined; const url = 'url' in source && typeof source.url === 'string' ? source.url : undefined; text += ` ${index + 1}. ${title ?? url ?? source.type}\n`; if (url) text += ` ${url}\n`; } for (const snippet of citation.snippets ?? []) { text += ` ${snippet.text}\n`; } } output.plain(text); } export async function runChat( { email, followUp, format, prompt, serverUrl }: ChatOptions, target: OutputTarget = process.stdout, ) { const client = await createGleanClient({ email, serverUrl }); const output = createMarkdownOutput(format, target); const firstStream = output.stream(); const firstTurn = await streamTurn(client, prompt, undefined, (delta) => firstStream.delta(delta), ); firstStream.complete(); if (firstTurn.completed) printCitations(firstTurn.completed, output); if (!followUp) return; if (!firstTurn.conversationId) { throw new Error( 'The first turn did not return a conversation_id; cannot continue the conversation.', ); } output.plain('\nFollow-up:\n'); const followUpStream = output.stream(); const followUpTurn = await streamTurn( client, followUp, firstTurn.conversationId, (delta) => followUpStream.delta(delta), ); followUpStream.complete(); if (followUpTurn.completed) printCitations(followUpTurn.completed, output); } ``` ### Report typed SDK failures Catch errors at the CLI boundary and preserve actionable HTTP status, platform error code, request ID, retry hints, timeout, and connection details without exposing credentials. src/errors.ts ``` import { ConnectionError, GleanBaseError, PlatformProblemDetailError, RequestTimeoutError, } from '@gleanwork/api-client/models/errors'; /** Converts typed SDK errors into actionable CLI output. */ export function formatSdkError(error: unknown): string { if (error instanceof PlatformProblemDetailError) { const retryAfter = error.headers.get('retry-after'); return [ `HTTP ${error.status}: ${error.detail}`, `Code: ${error.code}`, `Request ID: ${error.request_id}`, retryAfter ? `Retry after: ${retryAfter}` : undefined, ] .filter(Boolean) .join('\n'); } if (error instanceof GleanBaseError) { return `HTTP ${error.statusCode}: ${error.message}`; } if (error instanceof RequestTimeoutError) { return 'The request timed out. Try again or increase the SDK timeout.'; } if (error instanceof ConnectionError) { return `Could not reach Glean: ${error.message}`; } return error instanceof Error ? error.message : String(error); } ``` Architecture Your CLIprompt and follow-up TypeScript API clientcreateStream Platform Chatpermission-aware response EventStreamtyped createStream events Prerequisites Node.js 22.12.0 or newer. The steps use `npx` and `npm`. Install Node from https\://nodejs.org if needed. A Glean instance with content indexed Your work email, or the complete Glean backend HTTPS origin A tenant that permits the public OAuth client and `chat` scope through DCR Run it yourself 1 **Scaffold the project** Copies the runnable TypeScript Chat CLI and fixture tests into a new directory. OAuth login and secure token storage come from the pinned `@gleanwork/auth` package. ``` npx -y tiged@2.12.8 gleanwork/glean-cookbook/recipes/streaming-chat-with-citations streaming-chat-with-citations ``` 2 **Install dependencies** ``` cd streaming-chat-with-citations && npm install ``` 3 **Run the fixture tests** Runs Vitest with MSW-backed fixtures, without credentials or live network access, covering typed `createStream` events, exact-once delta composition, `conversation_id` propagation, citation separation, and typed SDK error formatting. ``` npm test ``` 4 **Sign in with OAuth** Discovers your Glean backend from work email and completes Authorization Code with PKCE for Chat and `offline_access`. Use `--server-url` for an explicit backend. If DCR is restricted, set `GLEAN_OAUTH_CLIENT_ID` for an administrator-provisioned public client. If OAuth is not available, set `GLEAN_API_TOKEN` later as a user-scoped fallback. ``` npm run login -- --email "" ``` 5 **Run one streamed Chat turn** Sends a question through `createStream` and prints grounded citation data against your own instance. Pipes receive each raw Markdown delta as it arrives. Interactive terminals buffer one complete answer and render it once. ``` npm run verify -- --email "" --prompt "" ``` 6 **Stream a follow-up** Starts a stored conversation, consumes `createStream` events for each answer, and sends a follow-up using the returned `conversation_id`. Raw output streams incrementally; terminal-rendered output is buffered per turn. ``` npm start -- --email "" --prompt "" --follow-up "" ``` `createStream` is SDK-only HTTP clients request SSE by setting `stream` to `true` in the JSON body. SDK callers use `createStream()` instead of setting `stream` on `create()`. OAuth policy varies by tenant DCR may be disabled or restricted, or may not grant Chat. Use an administrator-provisioned public client when available; a user-scoped `CHAT` token is the tutorial fallback. Terminal Markdown renders after each turn Interactive terminal mode consumes the API EventStream as it arrives but buffers one complete answer before rendering it once. Pipes and redirects receive each raw Markdown delta immediately and get a final newline on completion, so only terminal-rendered output is not token-by-token. Take it further * Render citation spans as links in a web interface using annotation `start_index` and `end_index`. * Persist `conversation_id` in an application session and resume it on the next request. * Add cancellation with `AbortController` when the Chat interaction moves into a UI. Try it **What's our PTO policy?** Returns a non-empty permission-aware streamed response with a `conversation_id` and at least one citation source when the user's indexed content contains a relevant policy. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:streaming-chat-with-citations ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/streaming-chat-with-citations) Runs the recipe through the Glean cookbook plugin. [Chat](/cookbook.md#chat) collection 1. 1Stream a cited Chat responseBeginner Auth Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (chat). At a glance CapabilitiesChat SurfacesPlatform API StatusQuickstart Time\~20 min Required scopes CHAT --- [Agents](/cookbook.md#agents) [ · ](/cookbook.md#agents) [1](/cookbook.md#agents) [ of](/cookbook.md#agents) [ ](/cookbook.md#agents) [3](/cookbook.md#agents) # Triage one support issue with a Glean Agent Give a published support-triage Agent one issue URL or description and get an evidence-backed summary, next diagnostic step, and customer-safe response. \~15 minBeginner Problem A support issue arrives with a URL or a short description, but the first response still requires someone to reconstruct the evidence, identify the next diagnostic, and write a safe customer update. Code walkthrough The runnable CLI resolves a refreshable Glean credential, builds an explicit USER prompt, calls the Platform Agents run endpoint, and prints only the Agent's structured response. ### Build an evidence-first triage prompt Keep the issue input explicit and require the Agent to separate observed evidence from hypotheses before producing a customer-safe response. scripts/triage.mjs ``` #!/usr/bin/env node import 'dotenv/config'; import { spawn } from 'node:child_process'; import { mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { createGleanTokenProvider } from '@gleanwork/auth'; export function requiredEnv(name, env = process.env) { const value = env[name]?.trim(); if (!value) throw new Error(`Missing required environment variable: ${name}`); return value; } export function parseAgentAnswer(response) { const answer = (response.messages ?? []) .filter((message) => message.role === 'GLEAN_AI') .flatMap((message) => message.content ?? []) .map((block) => block.text ?? '') .join('\n') .trim(); return { answer }; } export function buildTriagePrompt(issue) { return [ 'Triage this one support issue using only information available to you through Glean.', 'Return four concise sections: Summary, Evidence, Next diagnostic step, and Customer-safe response.', 'Separate observed evidence from hypotheses. If the indexed evidence is insufficient, say so instead of inventing a cause.', 'If the issue is an Intercom URL, try the URL and its conversation ID; if the live page is only an authenticated application shell, use the indexed ticket record and disclose that limitation.', '', `Support issue:\n${issue.trim()}`, ].join('\n'); } const transientRequestAttempts = 2; const transientRetryDelayMs = 1500; function fetchErrorDetail(error) { const cause = error?.cause; const causeDetail = cause?.code ?? cause?.message; return causeDetail ? `${error.message} (${causeDetail})` : error.message; } async function requestWithCurl(endpoint, apiToken, payload) { const directory = await mkdtemp(join(tmpdir(), 'glean-triage-')); const bodyPath = join(directory, 'request.json'); const config = [ `url = "${endpoint}"`, 'request = POST', `header = "Authorization: Bearer ${apiToken}"`, 'header = "Content-Type: application/json"', `data-binary = "@${bodyPath}"`, 'silent', 'show-error', 'write-out = "\\n__HTTP_STATUS__:%{http_code}"', ].join('\n'); try { await writeFile(bodyPath, JSON.stringify(payload), { mode: 0o600 }); const { stdout, stderr, status } = await new Promise((resolve, reject) => { const childEnv = { ...process.env }; delete childEnv.GLEAN_API_TOKEN; const child = spawn('curl', ['--config', '-'], { env: childEnv, stdio: ['pipe', 'pipe', 'pipe'], }); const stdoutChunks = []; const stderrChunks = []; const timeout = setTimeout(() => child.kill('SIGTERM'), 180_000); child.stdout.on('data', (chunk) => stdoutChunks.push(chunk)); child.stderr.on('data', (chunk) => stderrChunks.push(chunk)); child.on('error', reject); child.on('close', (code) => { clearTimeout(timeout); resolve({ stdout: Buffer.concat(stdoutChunks).toString(), stderr: Buffer.concat(stderrChunks).toString(), status: code, }); }); child.stdin.end(config); }); if (status !== 0) throw new Error(`curl failed: ${stderr.trim() || `exit ${status}`}`); const marker = '\n__HTTP_STATUS__:'; const markerIndex = stdout.lastIndexOf(marker); if (markerIndex < 0) throw new Error('curl returned no HTTP status'); return { status: Number(stdout.slice(markerIndex + marker.length).trim()), ok: Number(stdout.slice(markerIndex + marker.length).trim()) >= 200 && Number(stdout.slice(markerIndex + marker.length).trim()) < 300, text: stdout.slice(0, markerIndex), }; } finally { await rm(directory, { recursive: true, force: true }); } } export async function triageSupportIssue(issue, env = process.env) { const agentId = requiredEnv('GLEAN_AGENT_ID', env); const serverURL = requiredEnv('GLEAN_SERVER_URL', env); if (!issue?.trim()) throw new Error('Provide a support issue URL or description.'); const apiToken = await createGleanTokenProvider({ serverUrl: serverURL, scopes: ['agents'], })(); const prompt = buildTriagePrompt(issue); const endpoint = `${serverURL.replace(/\/+$/u, '')}/api/agents/${encodeURIComponent(agentId)}/runs`; const payload = { messages: [{ role: 'USER', content: [{ text: prompt, type: 'text' }] }], stream: false, }; let response; let lastFetchError; for (let attempt = 1; attempt <= transientRequestAttempts; attempt += 1) { try { response = await fetch(endpoint, { method: 'POST', headers: { authorization: `Bearer ${apiToken}`, 'content-type': 'application/json', }, body: JSON.stringify(payload), }); break; } catch (error) { lastFetchError = error; if (attempt < transientRequestAttempts) await new Promise((resolve) => setTimeout(resolve, transientRetryDelayMs), ); } } if (!response) { if (lastFetchError?.cause?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY') { response = await requestWithCurl(endpoint, apiToken, payload); } else { throw new Error( `Platform Agents API request failed after ${transientRequestAttempts} attempts: ${fetchErrorDetail(lastFetchError)}`, ); } } if (!response) { throw new Error( `Platform Agents API request failed after ${transientRequestAttempts} attempts: ${fetchErrorDetail(lastFetchError)}`, ); } const responseText = typeof response.text === 'function' ? await response.text() : response.text; let responseBody; try { responseBody = JSON.parse(responseText); } catch { responseBody = undefined; } if (!response.ok) { const detail = responseBody?.message || responseBody?.error || responseText; throw new Error( `Platform Agents API returned HTTP ${response.status}: ${String(detail).slice(0, 500)}`, ); } const parsed = parseAgentAnswer(responseBody ?? {}); if (!parsed.answer) { throw new Error( 'The Agent returned no answer text. Confirm it is conversational and published.', ); } return parsed; } if (process.argv[1]?.endsWith('/triage.mjs')) { const issue = process.argv.slice(2).join(' ') || process.env.GLEAN_SUPPORT_ISSUE; try { const { answer } = await triageSupportIssue(issue); console.log(answer); } catch (error) { console.error(`Support triage failed: ${error.message}`); process.exit(1); } } ``` ### Keep the fixture check credential-free Exercise response parsing against a recorded Agent response so the recipe can verify its output contract before making a live request. scripts/verify-fixture.mjs ``` #!/usr/bin/env node import assert from 'node:assert/strict'; import { parseAgentAnswer } from './triage.mjs'; const response = { messages: [ { role: 'USER', content: [{ text: 'ignored' }] }, { role: 'GLEAN_AI', content: [ { type: 'text', text: 'Summary\nThe support issue is a stale answer.' }, { type: 'text', text: '\nEvidence\nA recent document update is indexed.', }, ], }, ], }; const parsed = parseAgentAnswer(response); assert.match(parsed.answer, /Summary/u); assert.match(parsed.answer, /Evidence/u); assert.doesNotMatch(parsed.answer, /ignored/u); console.log( 'fixture verification passed: Platform Agent response parsing is stable', ); ``` Architecture Support issueOne ticket URL or issue description Glean Platform AgentRuns with the caller's permissions Triage outputEvidence, next diagnostic, and safe response Prerequisites Node 22.12 or newer A published conversational Agent configured to triage support issues using the user's permitted Glean content The Agent ID from Agent Builder or the Agents Platform API A work email for tenant discovery and OAuth sign-in, or a token able to run the selected Agent Run it yourself 1 **Scaffold the recipe** ``` npx -y tiged@2.12.8 --mode=git gleanwork/glean-cookbook/recipes/triage-support-issue triage-support-issue ``` 2 **Install dependencies** Installs dotenv. The recipe uses Node's built-in fetch against the Platform Agents API, not A2A or the legacy REST API. ``` cd triage-support-issue && npm install ``` 3 **Sign in to Glean** Use @gleanwork/auth for tenant discovery and refreshable OAuth credentials stored outside the project. If OAuth is unavailable, provide a user-scoped token that can run the selected Agent. ``` npm run login -- --email "" ``` 4 **Set the Agent and issue** Set GLEAN\_AGENT\_ID to a published conversational support-triage Agent. Then pass one ticket URL or issue description to the triage command. A form-triggered Agent is outside this quickstart because it requires a different input contract. 5 **Run the triage** The command makes one Platform Agent run and prints the structured triage. It does not make any external write or customer-facing update. ``` npm run triage -- "" ``` 6 **Verify the response** Run the no-credential fixture check first. For a live check, set GLEAN\_SUPPORT\_ISSUE and run npm run verify; confirm the Agent separates evidence from hypotheses. ``` npm run verify:fixture ``` Evidence before hypothesis The prompt asks the Agent to separate observed Glean evidence from hypotheses and to disclose when evidence is insufficient. No external write The beginner recipe only prints the Agent response; it does not update a ticket, send a message, or close an issue. The Agent is configured separately The recipe invokes a published conversational Agent. It does not teach Agent Builder, tool governance, or form-triggered input schemas. Take it further * Add a streaming response so a support engineer can see the Agent's progress. * Pass structured issue fields after you have a form-triggered Agent and its input schema is verified. * Pair this recipe with a Trigger that creates a triage request when a support-relevant event arrives. Try it **Triage this support issue: the customer reports that search results are stale after a document update.** The Agent returns non-empty Summary, Evidence, Next diagnostic step, and Customer-safe response sections, and clearly distinguishes evidence from hypotheses. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:triage-support-issue ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/triage-support-issue) Runs the recipe through the Glean cookbook plugin. [Agents](/cookbook.md#agents) collection 1. 1Triage one support issue with a Glean AgentBeginner 2. 2[Approve an agent action from a CLI](/cookbook/human-in-the-loop-agent.md)Beginner 3. 3[On-call Copilot](/cookbook/oncall-copilot.md)Advanced Auth Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (AGENTS). At a glance CapabilitiesAgents SurfacesPlatform API StatusQuickstart Time\~15 min Required scopes AGENTS --- [Skills](/cookbook.md#skills) [ · ](/cookbook.md#skills) [1](/cookbook.md#skills) [ of](/cookbook.md#skills) [ ](/cookbook.md#skills) [3](/cookbook.md#skills) # Validate skill publishing Validate a local SKILL.md, test publishing and retrieval in Glean, confirm the downloaded file matches, and delete the test skill. \~15 minBeginner Problem You want to confirm that a local SKILL.md can be published and retrieved through the Skills API. This quickstart first checks access with a generated sample, then tests your file. Each command compares the downloaded SKILL.md with the upload byte for byte and deletes the test skill. It does not extract or execute retrieved files. Code walkthrough The example uses the official SDK to validate your file before creating a skill. It retrieves the saved skill, compares the downloaded SKILL.md with the upload, and cleans up afterward. It never extracts files to disk or executes them. ### Configure the Skills API client Find your Glean instance and obtain credentials through the authentication library. Node.js loads optional environment settings; the recipe does not parse them itself. src/client.ts ``` import { loadEnvFile } from 'node:process'; import { Glean, type SDKOptions } from '@gleanwork/api-client'; import { createGleanTokenProvider, discoverGleanTenant } from '@gleanwork/auth'; const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']); export interface GleanClientTarget { email?: string; serverUrl?: string; } function loadDotEnv() { try { loadEnvFile(); } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; } } async function resolveServerUrl({ email, serverUrl }: GleanClientTarget) { const explicit = serverUrl?.trim(); if (explicit) return explicit; const workEmail = email?.trim(); if (workEmail) return (await discoverGleanTenant(workEmail)).serverUrl; const configured = process.env.GLEAN_SERVER_URL?.trim(); if (configured) return configured; throw new Error( 'Pass --email or --server-url, or set GLEAN_SERVER_URL in your environment.', ); } export async function createGleanClient( target: GleanClientTarget, log: (message: string) => void = () => undefined, ) { loadDotEnv(); const serverURL = await resolveServerUrl(target); const server = new URL(serverURL); const loopback = LOOPBACK_HOSTS.has(server.hostname); if ( (server.protocol !== 'https:' && !loopback) || server.username || server.password || server.search || server.hash || (server.pathname && server.pathname !== '/') || (!loopback && server.port) ) { throw new Error('Use a complete Glean backend HTTPS origin.'); } const staticToken = process.env.GLEAN_API_TOKEN?.trim(); let apiToken: string | ReturnType; if (staticToken) { log('Using GLEAN_API_TOKEN from the environment.'); apiToken = staticToken; } else { const scopes = ['SKILLS']; log('Using the OAuth session (SKILLS).'); apiToken = createGleanTokenProvider({ serverUrl: server.origin, scopes, }); } const options = { serverURL: server.origin, apiToken, timeoutMs: 30_000, retryConfig: { strategy: 'backoff', backoff: { initialInterval: 500, maxInterval: 5_000, exponent: 2, maxElapsedTime: 90_000, }, retryConnectionErrors: true, }, } satisfies SDKOptions; return new Glean(options); } ``` ### Create, retrieve, and delete a test skill Validate the file, create a skill without automatic retries, and retrieve it by the returned ID. Compare downloaded content with the upload, then delete the test skill even if comparison fails. Stop without deleting if the API returns a later version of an existing skill. src/workflow\.ts ``` import fs from 'node:fs/promises'; import path from 'node:path'; import { randomBytes } from 'node:crypto'; import type { Glean } from '@gleanwork/api-client'; import { PlatformProblemDetailError } from '@gleanwork/api-client/models/errors'; import { CleanupFailedError } from './errors.js'; import { readSkillMd, readStream, saveLatestContent, verifyDownloadedSkill, } from './skill-md.js'; export type SkillsApi = Pick< Glean['skills'], 'create' | 'delete' | 'list' | 'retrieve' | 'retrieveContent' | 'validate' >; export interface FirstPersistResult { id: string; displayName: string; version: number; minorVersion: number; contentPath: string; contentBytes: number; } function manifest(displayName: string) { return `---\nname: ${displayName}\ndescription: Test publishing and retrieving a skill with the Skills API.\n---\n\n# Publishing test\n\nThis sample tests creating and retrieving a skill. It contains no executable code.\n`; } function rethrow(error: unknown): never { throw error instanceof Error ? error : new Error('Verification failed.'); } export function cleanupCommand( skillId: string, auth: { email?: string; serverUrl?: string } = {}, ) { const parts = [`npm start -- cleanup --id ${skillId} --yes`]; if (auth.serverUrl?.trim()) { parts.push(`--server-url ${auth.serverUrl.trim()}`); } else if (auth.email?.trim()) { parts.push(`--email ${auth.email.trim()}`); } // With no flags, the retry uses the same .env/token path as the original run. return parts.join(' '); } export function verifiedSuccessLine(result: FirstPersistResult) { return `Verified ${result.displayName} (${result.id}) at version ${result.version}.${result.minorVersion}; downloaded ${result.contentBytes} byte(s); SKILL.md matches the upload; cleanup completed.`; } export async function findSkillById(api: SkillsApi, skillId: string) { let cursor: string | undefined; do { const page = await api.list(100, cursor); if (page.skills.some((skill) => skill.id === skillId)) return true; cursor = page.next_cursor ?? undefined; } while (cursor); return false; } export async function findSkillByName(api: SkillsApi, displayName: string) { let cursor: string | undefined; do { const page = await api.list(100, cursor); const match = page.skills.find( (skill) => skill.display_name === displayName, ); if (match) return match.id; cursor = page.next_cursor ?? undefined; } while (cursor); return undefined; } export async function deleteCapturedIds( api: SkillsApi, ids: string[], log: (message: string) => void, ) { const remaining: string[] = []; for (const id of ids) { log(`Deleting run-owned skill ${id}...`); try { await api.delete(id); } catch { remaining.push(id); } } return remaining; } async function rejectInvalidFrontmatter(api: SkillsApi, runRoot: string) { const invalidPath = path.join(runRoot, 'invalid', 'SKILL.md'); await fs.mkdir(path.dirname(invalidPath), { recursive: true }); await fs.writeFile(invalidPath, '# Missing frontmatter\n', { flag: 'wx', mode: 0o600, }); let rejected = false; try { await api.validate({ file: await readSkillMd(invalidPath) }); } catch (error) { if ( !(error instanceof PlatformProblemDetailError) || error.status !== 400 || ![ 'invalid_request', 'missing_required_field', 'invalid_parameter', ].includes(error.code) ) { throw error; } rejected = true; } if (!rejected) throw new Error('Invalid SKILL.md unexpectedly validated.'); } export async function verifyFirstPersist( api: SkillsApi, options: { workDir: string; cleanup: boolean; bundlePath?: string; auth?: { email?: string; serverUrl?: string }; log?: (message: string) => void; }, ): Promise { const log = options.log ?? (() => undefined); const uniqueName = `cookbook-validate-${randomBytes(8).toString('hex')}`; const runRoot = path.join(options.workDir, uniqueName); const skillPath = options.bundlePath ? path.resolve(options.bundlePath) : path.join(runRoot, 'SKILL.md'); const contentPath = path.join(runRoot, 'downloaded', `${uniqueName}.zip`); let createdId: string | undefined; let result: FirstPersistResult | undefined; let workError: unknown; await fs.mkdir(runRoot, { recursive: true, mode: 0o700 }); if (!options.bundlePath) { await fs.writeFile(skillPath, manifest(uniqueName), { flag: 'wx', mode: 0o600, }); } try { log('Validating the local SKILL.md without saving it...'); const bundle = await readSkillMd(skillPath); const validation = await api.validate({ file: bundle }); const displayName = validation.metadata.display_name; if (!options.bundlePath && displayName !== uniqueName) { throw new Error('Validation returned an unexpected skill name.'); } log('Confirming invalid frontmatter is rejected without a create call...'); await rejectInvalidFrontmatter(api, runRoot); if (options.bundlePath) { const existing = await findSkillByName(api, displayName); if (existing) { throw new Error( `A skill named "${displayName}" already exists as ${existing}. Choose an unused name; this quickstart does not add versions.`, ); } } log('Publishing the skill once...'); // Create can add a version to an existing name. Never retry an ambiguous write. const created = await api.create( { file: bundle }, { retries: { strategy: 'none' } }, ); if ( created.skill.latest_version !== 1 || created.skill.latest_minor_version !== 1 ) { throw new Error( `Create returned version ${created.skill.latest_version}.${created.skill.latest_minor_version} for ${created.skill.id}; expected a new skill at version 1.1. This may be an existing skill; it was not deleted. Inspect it before continuing.`, ); } createdId = created.skill.id; if (created.skill.display_name !== displayName) { throw new Error('Created skill name does not match validated metadata.'); } log('Confirming list and get return the captured ID...'); if (!(await findSkillById(api, createdId))) { throw new Error('List did not include the skill this run just created.'); } const retrieved = await api.retrieve(createdId); if (retrieved.skill.id !== createdId) { throw new Error('Direct retrieval returned a different skill.'); } log('Downloading the skill ZIP and comparing SKILL.md with the upload...'); const response = await api.retrieveContent(createdId); const bytes = await readStream(response.result); if (bytes.byteLength === 0) { throw new Error('Latest skill content was empty.'); } await verifyDownloadedSkill(bytes, bundle.content); const saved = await saveLatestContent(bytes, contentPath); result = { id: createdId, displayName: created.skill.display_name, version: created.skill.latest_version, minorVersion: created.skill.latest_minor_version, contentPath: saved, contentBytes: bytes.byteLength, }; } catch (error) { workError = error; } const remaining = createdId && options.cleanup ? await deleteCapturedIds(api, [createdId], log) : []; await fs.rm(runRoot, { recursive: true, force: true }); if (remaining.length > 0) { throw new CleanupFailedError( remaining, remaining.map((id) => cleanupCommand(id, options.auth)).join('\n '), workError, ); } if (workError) rethrow(workError); if (!result) throw new Error('Verification did not produce a result.'); return result; } ``` ### Compare the stored file with the upload Use yauzl to read the ZIP in memory. Require one regular SKILL.md at the root, bound decompression by the uploaded file size, check its checksum, and compare bytes. Do not compare ZIP bytes: compression and timestamps can differ. src/skill-md.ts ``` import fs from 'node:fs/promises'; import path from 'node:path'; import { Readable } from 'node:stream'; import { crc32 } from 'node:zlib'; import { fromBufferPromise } from 'yauzl'; export const MAX_CONTENT_BYTES = 10 * 1024 * 1024; export async function readSkillMd(filePath: string) { const resolved = path.resolve(filePath); const stats = await fs.stat(resolved); if (!stats.isFile() || path.basename(resolved) !== 'SKILL.md') { throw new Error('Provide a local SKILL.md file.'); } return { fileName: 'SKILL.md', content: new Uint8Array(await fs.readFile(resolved)), }; } export async function readStream( stream: { getReader(): ReadableStreamDefaultReader }, maxBytes = MAX_CONTENT_BYTES, ): Promise { const reader = stream.getReader(); const chunks: Uint8Array[] = []; let total = 0; while (true) { const { done, value } = await reader.read(); if (done) break; total += value.byteLength; if (total > maxBytes) { await reader.cancel(); throw new Error(`Downloaded skill content exceeds ${maxBytes} bytes.`); } chunks.push(value); } return Buffer.concat(chunks); } // A single Markdown upload is returned as a ZIP with one root SKILL.md. // Compare file bytes, not ZIP bytes: archive timestamps and compression can change. export async function verifyDownloadedSkill( archive: Buffer, uploaded: Uint8Array, ): Promise { const zip = await fromBufferPromise(archive, { lazyEntries: true, strictFileNames: true, }); try { const layoutError = 'Downloaded bundle must contain exactly one regular SKILL.md file.'; if (zip.entryCount !== 1) throw new Error(layoutError); for await (const entry of zip.eachEntry()) { const fileType = (entry.externalFileAttributes >>> 16) & 0o170000; if ( entry.fileName !== 'SKILL.md' || (entry.externalFileAttributes & 0x10) !== 0 || // DOS directory flag (fileType !== 0 && fileType !== 0o100000) ) { throw new Error(layoutError); } if (entry.uncompressedSize !== uploaded.byteLength) { throw new Error( 'Downloaded SKILL.md does not match the uploaded file.', ); } const stream = await zip.openReadStreamPromise(entry); const content = await readStream( Readable.toWeb(stream), uploaded.byteLength, ); // yauzl validates sizes and decompression, but does not verify CRC-32. if (crc32(content) !== entry.crc32) { throw new Error('Downloaded SKILL.md failed its ZIP checksum.'); } if (!content.equals(Buffer.from(uploaded))) { throw new Error( 'Downloaded SKILL.md does not match the uploaded file.', ); } } } finally { zip.close(); } } export async function saveLatestContent( bytes: Buffer, destination: string, ): Promise { const resolved = path.resolve(destination); await fs.mkdir(path.dirname(resolved), { recursive: true, mode: 0o700 }); await fs.writeFile(resolved, bytes, { flag: 'wx', mode: 0o600 }); return resolved; } ``` Architecture Your SKILL.mdsample SKILL.md, or --bundle Validationcheck the file without saving it Skills APIcreate, list, get, content Latest contentcompare SKILL.md bytes; never execute Prerequisites Node.js 22.12.0 or newer A Glean instance with Skills enabled Your work email, or the complete Glean backend HTTPS origin Permission to create and delete test skills, using the SKILLS OAuth scope or the SKILLS permission on a user-scoped token. Run it yourself 1 **Copy the project onto your machine** Copy the runnable TypeScript Skills CLI, sample SKILL.md, and credential-free fixture tests into a new directory. OAuth uses the official @gleanwork/auth package. ``` npx -y tiged@2.12.8 gleanwork/glean-cookbook/recipes/validate-and-publish-skill validate-and-publish-skill ``` 2 **Install dependencies** Enter the project directory and install its dependencies. Run the remaining commands from this directory. ``` cd validate-and-publish-skill && npm install ``` 3 **Run the fixture tests** Run the tests without real credentials or network access. Workflow tests use the real SDK with MSW HTTP handlers to check content comparison and failure handling. Unhandled requests fail. CLI smoke tests cover help and local argument handling. Passing these tests does not verify access to your Glean instance. ``` npm test ``` 4 **Choose an authentication path** OAuth is the default. @gleanwork/auth discovers your instance, registers the OAuth client dynamically, and stores credentials securely. If OAuth is unavailable, copy .env.example to .env, set GLEAN\_SERVER\_URL and a user-scoped GLEAN\_API\_TOKEN with the SKILLS permission, and skip the next step. Omit --email from the remaining commands. If email discovery finds the wrong instance, replace --email with --server-url and your complete backend HTTPS origin on the login and live commands. 5 **Sign in with OAuth** Run the login command with the SKILLS scope and complete authorization in your browser. Wait for the terminal command to report success before continuing. Skip this step if you configured a token in .env. ``` npm run login -- --email "" ``` 6 **Verify against your instance** Create a uniquely named sample skill, retrieve it, compare the downloaded SKILL.md byte for byte with the upload, and permanently delete it. This verifies authentication and Skills API access before you use your own file. A mismatch fails verification but still triggers cleanup. With token authentication, run npm run verify without --email. ``` npm run verify -- --email "" ``` 7 **Test your own SKILL.md** Pass the path to your SKILL.md. Choose an unused name and do not publish that name concurrently. The command validates, creates, retrieves, compares, and then permanently deletes the test skill. With token authentication, omit --email. ``` npm start -- --bundle "" --email "" --yes ``` Validate before creating a skill Call validate first and stop on any error. Validation itself never creates or updates a skill. Use a new name The API can add a version when a name already exists. Use an unused name and avoid concurrent runs with that name. Creation is not retried automatically. If creation times out, inspect your instance before running it again; a skill may have been saved without returning its ID. Treat content as untrusted Limit the ZIP download to 10 MiB. Read its single regular root SKILL.md in memory, with decompression bounded by the uploaded file size. Require a valid checksum and exact file bytes. Do not extract files to disk or execute retrieved content. Delete by captured ID only Deletion is permanent. Clean up only the new skill identified by the create response, never a name-search result. New skills start at version 1.1. If the API returns any other version, stop without deleting it. If deletion fails, the command reports the remaining ID and exits with an error. Publishing is not execution The API stores and distributes skill bundles. This quickstart verifies the bytes of a single uploaded SKILL.md, not multi-file bundles or skill execution. It does not run the skill or test how an agent host interprets it. Sign-in permissions vary by instance Your instance must allow the SKILLS OAuth scope or the SKILLS permission on a user-scoped token. Take it further * Use the skill-publishing-pipeline recipe to publish another version, inspect a specific version, and examine a downloaded archive safely. * Use the GitHub import recipe when the source of truth is a public repository rather than a local SKILL.md. * After you can create and retrieve a skill, use its ID to explore version management and enabling or disabling it. Try it **Validate and test publishing a uniquely named sample skill, then delete the captured ID** Stdout ends with Verified \ (\) at version 1.1; downloaded \ byte(s); SKILL.md matches the upload; cleanup completed. The downloaded ZIP must contain one regular root SKILL.md whose checksum and bytes match the upload. Invalid archives or mismatches fail verification and still trigger cleanup. If delete fails, the process exits non-zero, prints the remaining ID and a cleanup command that includes --id, --yes, and the same --email or --server-url used for the run, and does not print that success line. Run this recipe * Choose your tool Claude CodeCodexCursor Install the pluginTerminalIn Claude Code 1 ``` claude plugin marketplace add gleanwork/glean-cookbook ``` 2 ``` claude plugin install cookbook@glean-cookbook ``` Run the recipe ``` /cookbook:validate-and-publish-skill ``` Run /reload-plugins to pick it up without restarting. [View source](https://github.com/gleanwork/glean-cookbook/tree/main/recipes/validate-and-publish-skill) Runs the recipe through the Glean cookbook plugin. [Skills](/cookbook.md#skills) collection 1. 1Validate skill publishingBeginner 2. 2[Import a skill from GitHub](/cookbook/import-skill-from-github.md)Intermediate 3. 3[Publish a versioned skill bundle](/cookbook/skill-publishing-pipeline.md)Intermediate Auth Run the authenticate step on this page. It discovers your tenant from work email and signs you in with [OAuth](/api-info/client/authentication/oauth.md), using the shipped login command. If OAuth is unavailable, create a scoped [Glean-issued token](/api-info/client/authentication/glean-issued.md) in [Token Management](https://app.glean.com/admin/platform/tokenManagement?tab=client) (SKILLS). At a glance CapabilitiesSkills SurfacesPlatform API StatusQuickstart Time\~15 min Required scopes SKILLS --- # Deprecations[](/deprecations.xml "Subscribe to RSS feed") This page lists all deprecations related to Glean's public APIs. Each deprecation includes the date it was introduced, the removal date after which the property will no longer be available, and guidance on what to use instead. See [API Evolution & Deprecations](/deprecations/overview.md) for details on how our deprecation process works. Filter deprecations:All upcoming (all) ## Removal date: 2026-10-15 POST`/api/index/v1/getdocumentcount` Indexing API `POST /api/index/v1/getdocumentcount` Endpoint is deprecated Deprecation introduced on 2026-02-03 POST`/api/index/v1/getdocumentstatus` Indexing API `POST /api/index/v1/getdocumentstatus` Endpoint is deprecated Deprecation introduced on 2026-02-03 POST`/api/index/v1/getusercount` Indexing API `POST /api/index/v1/getusercount` Endpoint is deprecated Deprecation introduced on 2026-02-03 POST`/api/index/v1/indexemployee` Indexing API `location` Field is deprecated Deprecation introduced on 2026-02-05 POST`/rest/api/v1/chat` Client API `citations` Use inline citations via ChatMessageFragment.citation and ChatMessageCitation.referenceRanges instead. This field is still populated for backward compatibility. Deprecation introduced on 2026-02-06 POST`/rest/api/v1/createannouncement` Client API `isNegated` Use relationType instead Deprecation introduced on 2026-02-05 `documents` Field is deprecated Deprecation introduced on 2026-02-05 `boardId` Answer Boards no longer supported Deprecation introduced on 2026-02-05 `snippet` Use 'text' field instead Deprecation introduced on 2026-02-05 `inviteTime` Use ChannelInviteInfo instead Deprecation introduced on 2026-02-05 `reminderTime` Use ChannelInviteInfo instead Deprecation introduced on 2026-02-05 `reactions` Use reacts instead Deprecation introduced on 2026-02-05 `role` Use permissions instead Deprecation introduced on 2026-02-05 GET`/rest/api/v1/governance/data/policies/{id}` Client API `sensitiveInfoTypes` Use sensitiveContentOptions instead Deprecation introduced on 2026-02-05 `likelihoodThreshold` Field is deprecated Deprecation introduced on 2026-02-05 `urlGreenlist` Field is deprecated Deprecation introduced on 2026-02-05 `datasources` Use datasourceInstances instead Deprecation introduced on 2026-02-05 `externalSharingOptions` Use broadSharingOptions instead Deprecation introduced on 2026-02-05 `enabled` Field is deprecated Deprecation introduced on 2026-02-05 `anyoneWithLinkEnabled` Field is deprecated Deprecation introduced on 2026-02-05 `sensitiveInfoTypes` Use customSensitiveExpressions instead Deprecation introduced on 2026-02-05 `sensitiveTerms` Use customSensitiveExpressions instead Deprecation introduced on 2026-02-05 `sensitiveRegexes` Use customSensitiveExpressions instead Deprecation introduced on 2026-02-05 POST`/rest/api/v1/listanswers` Client API `POST /rest/api/v1/listanswers` Answer boards have been removed and this endpoint no longer serves a purpose Deprecation introduced on 2026-01-21 ## Removal date: 2027-01-15 POST`/api/index/v1/bulkindexemployees` Indexing API `POST /api/index/v1/bulkindexemployees` Endpoint is deprecated Deprecation introduced on 2026-02-03 POST`/rest/api/v1/getanswer` Client API `trackingToken` Use \`answer.trackingToken\` instead. Deprecation introduced on 2026-05-07 POST`/rest/api/v1/getcollection` Client API `trackingToken` Use \`collection.trackingToken\` instead. Deprecation introduced on 2026-05-07 POST`/rest/api/v1/insights` Client API `searchSessionSatisfaction` This property is no longer supported. Please contact Support for alternatives. Deprecation introduced on 2026-05-13 ## Removal date: 2027-04-15 GET`/rest/api/v1/agents/{agent_id}` Client API `GET /rest/api/v1/agents/{agent_id}` Use GET /api/agents/{agent\_id} instead. Deprecation introduced on 2026-08-25 [More details](https://developers.glean.com/api/platform-api/agents-overview) GET`/rest/api/v1/agents/{agent_id}/schemas` Client API `GET /rest/api/v1/agents/{agent_id}/schemas` Use GET /api/agents/{agent\_id}/schemas instead. Deprecation introduced on 2026-08-25 [More details](https://developers.glean.com/api/platform-api/agents-overview) POST`/rest/api/v1/agents/runs/stream` Client API `POST /rest/api/v1/agents/runs/stream` Use POST /api/agents/{agent\_id}/runs with stream=true instead. Deprecation introduced on 2026-08-25 [More details](https://developers.glean.com/api/platform-api/agents-overview) POST`/rest/api/v1/agents/runs/wait` Client API `POST /rest/api/v1/agents/runs/wait` Use POST /api/agents/{agent\_id}/runs instead. Deprecation introduced on 2026-08-25 [More details](https://developers.glean.com/api/platform-api/agents-overview) POST`/rest/api/v1/agents/search` Client API `POST /rest/api/v1/agents/search` Use POST /api/agents/search instead. Deprecation introduced on 2026-08-25 [More details](https://developers.glean.com/api/platform-api/agents-overview) --- # API Evolution & Deprecations We evolve our API through additive changes and predictable deprecations — not versioned URLs. This ensures your integrations remain stable while allowing us to improve the API. *** ## What's a Breaking Change?[​](#whats-a-breaking-change "Direct link to What's a Breaking Change?") ### Breaking Changes Require your code to be updated: * Removing or renaming endpoints, fields, or parameters * Changing data types or formats * Making optional fields required * Tightening validation rules ### Non-Breaking Changes Work with your existing code: * Adding new endpoints or fields * Adding optional parameters * Loosening validation * Performance improvements *** ## The Deprecation Timeline[​](#the-deprecation-timeline "Direct link to The Deprecation Timeline") When we need to make a breaking change, we follow a predictable process: 1 ### Announce Deprecation We mark the feature as deprecated in our API responses and in our documentation. Your code continues to work normally. 2 ### Migration Period (6+ months) Both old and new approaches work side-by-side. We provide migration guides and send email notifications. 3 ### Removal at Fixed Date The deprecated feature is removed only on a fixed date: **Jan 15**, **Apr 15**, **Jul 15**, or **Oct 15** (23:59:59 UTC). This gives you a predictable timeline to plan for a migration. #### Example:[​](#example "Direct link to Example:") Consider a field deprecated on **2026-01-01**. The field will be removed on **2026-07-15** (a minimum of 6 months after deprecation, plus a short buffer, rounded up to the next quarterly removal date — removals only happen on **Jan 15, Apr 15, Jul 15, or Oct 15**) ``` 2026-01-01 2026-07-15 │ │ ▼ ▼ ────────────●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━●──────────── │ │ Deprecation Introduced Deprecation Removed ├──────────────────── Migration Period ────────────────────────┤ ``` *** ## How You'll Know About Changes[​](#how-youll-know-about-changes "Direct link to How You'll Know About Changes") ### API Headers Every response using deprecated features includes an `X-Glean-Deprecated` header with full details. ### Documentation Inline warnings on API pages, migration guides, and a complete [deprecation schedule](/deprecations/.md). When you use a deprecated feature, the API response includes an `X-Glean-Deprecated` header: ``` X-Glean-Deprecated: name=snippetText;kind=property;context=response;schema=SearchResultSnippet;removal=2026-07-15;docs=https://developers.glean.com/deprecations ``` Each deprecated item you use produces its own `X-Glean-Deprecated` header instance. The header value is a semicolon-separated list of `key=value` pairs: | Field | Description | | --------- | ------------------------------------------------------------------------- | | `name` | Name of the deprecated item | | `kind` | Type of deprecation: `endpoint`, `property`, `parameter`, or `enum-value` | | `context` | Where the deprecation applies: `request`, `response`, or `endpoint` | | `schema` | The schema containing the deprecated property (when applicable) | | `removal` | Date the item will be removed (ISO-8601) | | `docs` | Link to migration documentation (when available) | | `value` | The deprecated enum value (only for `enum-value` deprecations) | Automated Monitoring Parse this header in your integration tests or monitoring to catch deprecations early. *** ## Testing Future Changes[​](#testing-future-changes "Direct link to Testing Future Changes") Validate your integration before deprecated features are removed by including the `X-Glean-Exclude-Deprecated-After` header: ``` X-Glean-Exclude-Deprecated-After: 2027-01-15 ``` This header simulates how the API will behave after the deprecation date, helping you verify your code handles the changes correctly. ### Using the SDKs[​](#using-the-sdks "Direct link to Using the SDKs") Glean's official SDKs can be configured to automatically include this header on all requests through constructor options or environment variables. See the documentation for your SDK: * [TypeScript](https://github.com/gleanwork/api-client-typescript#experimental-features-and-deprecation-testing) * [Python](https://github.com/gleanwork/api-client-python#experimental-features-and-deprecation-testing) * [Go](https://github.com/gleanwork/api-client-go#experimental-features-and-deprecation-testing) * [Java](https://github.com/gleanwork/api-client-java#experimental-features-and-deprecation-testing) *** ## After Removal[​](#after-removal "Direct link to After Removal") When deprecated items are removed, the API provides clear error responses: | Item Type | Response | | ------------------- | -------------------------------------- | | **Endpoints** | `410 Gone` with migration instructions | | **Request fields** | `400 Bad Request` with details | | **Response fields** | Simply omitted from the response | Example error response ``` { "error": { "code": "deprecated_field_removed", "message": "Field 'userId' was removed on 2027-01-15. Use 'userIdentifier' instead.", "docs": "https://developers.glean.com/docs/migrations/2027-01-15" } } ``` --- # Docs MCP Server Give your AI coding assistant direct access to Glean's public developer documentation. Query API references, explore integration guides, and find code examples without leaving your editor. This server does not connect to your organization's Glean tenant, indexed knowledge, or configured tools. **Server URL:** `https://developers.glean.com/mcp` ## Available Tools[​](#available-tools "Direct link to Available Tools") The Docs MCP server provides two tools to your AI assistant: | Tool | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------- | | **`docs_search`** | Search across the documentation index with relevance ranking. Returns page titles, URLs, and snippets. | | **`docs_fetch`** | Retrieve the full content of any documentation page as markdown. Use URLs from search results or direct page links. | ## Install[​](#install "Direct link to Install") Ready to connect? [Click here to install](#) using your preferred IDE. ## Usage Examples[​](#usage-examples "Direct link to Usage Examples") Once connected, your AI assistant can answer questions about Glean's APIs: * "What authentication methods does Glean support?" * "How do I create a custom datasource with the Indexing API?" * "Show me the Python SDK usage for the Chat API" * "What are the required fields for indexing a document?" ## Related[​](#related "Direct link to Related") To connect an AI host to your organization's Glean tenant, indexed knowledge, and enabled tools, use the [Remote MCP Server](/guides/mcp/.md). If a tenant connection fails, see [Remote MCP troubleshooting](/guides/mcp/troubleshooting.md). --- # Platform API Errors Find the stable `ProblemDetail.code` returned by the Platform API, then open its reference page for likely causes, remediation, retry guidance, and an example response. Find an error Enter a code such as `invalid_cursor`, an HTTP status such as `429`, or a keyword such as `permissions`. Search error codes and descriptions 23 documented errors | Error code | HTTP status | What it means | | -------------------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------- | | [`invalid_request`](/errors/invalid-request.md "Invalid Request") | `400` | The request could not be processed because the payload, parameters, or request structure is invalid. | | [`missing_required_field`](/errors/missing-required-field.md "Missing Required Field") | `400` | The request is missing a field that the endpoint requires. | | [`invalid_parameter`](/errors/invalid-parameter.md "Invalid Parameter") | `400` | A request parameter or field is present but has an invalid value. | | [`invalid_cursor`](/errors/invalid-cursor.md "Invalid Pagination Cursor") | `400` | The pagination cursor cannot be used for this request. | | [`expired_cursor`](/errors/expired-cursor.md "Expired Pagination Cursor") | `400` | The pagination cursor was valid previously but is no longer usable. | | [`invalid_filter`](/errors/invalid-filter.md "Invalid Filter") | `400` | A filter in the request is not valid for the endpoint or datasource scope. | | [`invalid_datasource`](/errors/invalid-datasource.md "Invalid Datasource") | `400` | The requested datasource scope is unknown, inaccessible, or not valid for the operation. | | [`authentication_required`](/errors/authentication-required.md "Authentication Required") | `401` | The API request did not include valid authentication credentials. | | [`token_expired`](/errors/token-expired.md "Token Expired") | `401` | The authentication token was valid but has expired. | | [`insufficient_permissions`](/errors/insufficient-permissions.md "Insufficient Permissions") | `403` | The caller is authenticated but is not allowed to perform the requested operation. | | [`spend_limit_exceeded`](/errors/spend-limit-exceeded.md "Spend Limit Exceeded") | `403` | The request was rejected because a configured spend limit for the deployment has been reached. | | [`resource_not_found`](/errors/resource-not-found.md "Resource Not Found") | `404` | The requested resource or endpoint could not be found or is not visible to the caller. | | [`method_not_allowed`](/errors/method-not-allowed.md "Method Not Allowed") | `405` | The endpoint exists but does not support the HTTP method used by the request. | | [`request_timeout`](/errors/request-timeout.md "Request Timeout") | `408` | The server timed out while processing the request. | | [`request_too_large`](/errors/request-too-large.md "Request Too Large") | `413` | The request body is larger than the API accepts. | | [`token_limit_exceeded`](/errors/token-limit-exceeded.md "Token Limit Exceeded") | `413` | The request exceeded the model's token limit, which caps how much content the model can process in one operation. | | [`conflict`](/errors/conflict.md "Conflict") | `409` | The request conflicts with the current state of the resource. | | [`gone`](/errors/gone.md "Gone") | `410` | The resource or operation is no longer available. | | [`unprocessable_query`](/errors/unprocessable-query.md "Unprocessable Query") | `422` | The API understood the request shape but could not process the query semantics. | | [`tools_unauthorized`](/errors/tools-unauthorized.md "Tools Unauthorized") | `422` | The request targets an agent that uses tools the end user has not authorized, so the run cannot start. | | [`rate_limit_exceeded`](/errors/rate-limit-exceeded.md "Rate Limit Exceeded") | `429` | The caller has sent too many requests in a time window. | | [`internal_error`](/errors/internal-error.md "Internal Error") | `500` | The API encountered an unexpected error while processing the request. | | [`service_unavailable`](/errors/service-unavailable.md "Service Unavailable") | `503` | The API is temporarily unable to serve the request. | --- # Authentication Required ## Code[​](#code "Direct link to Code") * `code`: `authentication_required` * `status`: `401` * `url`: `https://developers.glean.com/errors/authentication-required` ## What It Means[​](#what-it-means "Direct link to What It Means") The API request did not include valid authentication credentials. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The `Authorization` header is missing. * The token is malformed or not recognized. * The request is being sent to the wrong backend domain for the token. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Include a valid bearer token in the `Authorization` header. * Confirm the token belongs to the Glean instance receiving the request. ### Admin Actions[​](#admin-actions "Direct link to Admin Actions") * Create or rotate API tokens from the appropriate admin console workflow. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry after supplying valid credentials. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/authentication-required", "title": "Authentication Required", "status": 401, "detail": "Human-readable explanation specific to this occurrence.", "code": "authentication_required", "documentation_url": "https://developers.glean.com/errors/authentication-required", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Authentication Overview](/get-started/authentication.md) --- # Conflict ## Code[​](#code "Direct link to Code") * `code`: `conflict` * `status`: `409` * `url`: `https://developers.glean.com/errors/conflict` ## What It Means[​](#what-it-means "Direct link to What It Means") The request conflicts with the current state of the resource. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The resource already exists. * The requested update is stale or conflicts with another change. * The operation cannot be completed while the resource is in its current state. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Fetch the latest resource state before retrying the change. * Adjust the request so it no longer conflicts with existing data. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retrying unchanged may continue to fail. Retry after reconciling state. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/conflict", "title": "Conflict", "status": 409, "detail": "Human-readable explanation specific to this occurrence.", "code": "conflict", "documentation_url": "https://developers.glean.com/errors/conflict", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Expired Pagination Cursor ## Code[​](#code "Direct link to Code") * `code`: `expired_cursor` * `status`: `400` * `url`: `https://developers.glean.com/errors/expired-cursor` ## What It Means[​](#what-it-means "Direct link to What It Means") The pagination cursor was valid previously but is no longer usable. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The cursor was stored and reused after its validity window ended. * The underlying result set changed enough that the cursor can no longer continue. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Start a new search or list request and continue from the new cursor. * Avoid persisting cursors as long-lived bookmarks. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retrying with the expired cursor will fail. Retry from a fresh first page. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/expired-cursor", "title": "Expired Pagination Cursor", "status": 400, "detail": "Human-readable explanation specific to this occurrence.", "code": "expired_cursor", "documentation_url": "https://developers.glean.com/errors/expired-cursor", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Gone ## Code[​](#code "Direct link to Code") * `code`: `gone` * `status`: `410` * `url`: `https://developers.glean.com/errors/gone` ## What It Means[​](#what-it-means "Direct link to What It Means") The resource or operation is no longer available. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The resource was deleted. * The operation targets an expired or retired object. * The endpoint no longer supports the requested workflow. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Stop using the stale resource or workflow. * Create a new resource or switch to the replacement documented by the endpoint. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Do not retry the same request unless the resource is recreated or the workflow changes. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/gone", "title": "Gone", "status": 410, "detail": "Human-readable explanation specific to this occurrence.", "code": "gone", "documentation_url": "https://developers.glean.com/errors/gone", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Insufficient Permissions ## Code[​](#code "Direct link to Code") * `code`: `insufficient_permissions` * `status`: `403` * `url`: `https://developers.glean.com/errors/insufficient-permissions` ## What It Means[​](#what-it-means "Direct link to What It Means") The caller is authenticated but is not allowed to perform the requested operation. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The token lacks the required scopes or permissions. * The authenticated user or app cannot access the requested resource. * The resource is intentionally hidden from callers without access. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Check that the integration is using the intended token and scopes. * Use the response `detail` and endpoint documentation to identify required access. ### Admin Actions[​](#admin-actions "Direct link to Admin Actions") * Grant the needed permissions or use a token associated with an authorized principal. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry only after permissions or scopes have been corrected. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/insufficient-permissions", "title": "Insufficient Permissions", "status": 403, "detail": "Human-readable explanation specific to this occurrence.", "code": "insufficient_permissions", "documentation_url": "https://developers.glean.com/errors/insufficient-permissions", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Authentication Overview](/get-started/authentication.md) --- # Internal Error ## Code[​](#code "Direct link to Code") * `code`: `internal_error` * `status`: `500` * `url`: `https://developers.glean.com/errors/internal-error` ## What It Means[​](#what-it-means "Direct link to What It Means") The API encountered an unexpected error while processing the request. ## Common Causes[​](#common-causes "Direct link to Common Causes") * A transient server-side problem occurred. * The request triggered a condition the API could not complete. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Retry once or twice with backoff if the operation is safe to retry. * Record the `request_id` for support if the error persists. ### Admin Actions[​](#admin-actions "Direct link to Admin Actions") * Share the `request_id`, timestamp, endpoint, and high-level request context with support. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry cautiously with backoff. Do not retry indefinitely. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/internal-error", "title": "Internal Error", "status": 500, "detail": "Human-readable explanation specific to this occurrence.", "code": "internal_error", "documentation_url": "https://developers.glean.com/errors/internal-error", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Invalid Pagination Cursor ## Code[​](#code "Direct link to Code") * `code`: `invalid_cursor` * `status`: `400` * `url`: `https://developers.glean.com/errors/invalid-cursor` ## What It Means[​](#what-it-means "Direct link to What It Means") The pagination cursor cannot be used for this request. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The cursor is malformed, truncated, or copied incorrectly. * The cursor came from a different endpoint, query, or request shape. * The client changed pagination-related inputs between requests. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Use cursors only with the same endpoint and request shape that returned them. * Start a new request and use the fresh cursor returned in that response. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Do not retry with the same cursor. Start pagination again. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/invalid-cursor", "title": "Invalid Pagination Cursor", "status": 400, "detail": "Human-readable explanation specific to this occurrence.", "code": "invalid_cursor", "documentation_url": "https://developers.glean.com/errors/invalid-cursor", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Platform Search API](/api/platform-api/search-overview.md) --- # Invalid Datasource ## Code[​](#code "Direct link to Code") * `code`: `invalid_datasource` * `status`: `400` * `url`: `https://developers.glean.com/errors/invalid-datasource` ## What It Means[​](#what-it-means "Direct link to What It Means") The requested datasource scope is unknown, inaccessible, or not valid for the operation. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The datasource type or instance name is misspelled. * The caller does not have access to the datasource. * The request combines datasource scopes that the endpoint cannot process together. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Verify datasource identifiers against the integration configuration. * Remove inaccessible or unsupported datasource scopes. ### Admin Actions[​](#admin-actions "Direct link to Admin Actions") * Confirm the datasource is connected and visible to the integration. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry only after correcting the datasource scope or access. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/invalid-datasource", "title": "Invalid Datasource", "status": 400, "detail": "Human-readable explanation specific to this occurrence.", "code": "invalid_datasource", "documentation_url": "https://developers.glean.com/errors/invalid-datasource", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Platform Search API](/api/platform-api/search-overview.md) --- # Invalid Filter ## Code[​](#code "Direct link to Code") * `code`: `invalid_filter` * `status`: `400` * `url`: `https://developers.glean.com/errors/invalid-filter` ## What It Means[​](#what-it-means "Direct link to What It Means") A filter in the request is not valid for the endpoint or datasource scope. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The filter field name is misspelled. * The field is not supported for the requested datasource or endpoint. * The filter value does not match the expected type or format. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Check the endpoint documentation for supported filters. * Validate field names and values before sending the request. * Use response `detail` or field-level `errors` to identify the offending filter. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry only after correcting or removing the invalid filter. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/invalid-filter", "title": "Invalid Filter", "status": 400, "detail": "Human-readable explanation specific to this occurrence.", "code": "invalid_filter", "documentation_url": "https://developers.glean.com/errors/invalid-filter", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Platform Search API](/api/platform-api/search-overview.md) --- # Invalid Parameter ## Code[​](#code "Direct link to Code") * `code`: `invalid_parameter` * `status`: `400` * `url`: `https://developers.glean.com/errors/invalid-parameter` ## What It Means[​](#what-it-means "Direct link to What It Means") A request parameter or field is present but has an invalid value. ## Common Causes[​](#common-causes "Direct link to Common Causes") * A field has the wrong type, format, or enum value. * A string, list, or number is outside the allowed range. * A field-level validation error is included in the `errors` array. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Use the response `detail` and any field-level `errors` to identify the invalid value. * Validate request values before sending them to the API. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry only after correcting the invalid value. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/invalid-parameter", "title": "Invalid Parameter", "status": 400, "detail": "Human-readable explanation specific to this occurrence.", "code": "invalid_parameter", "documentation_url": "https://developers.glean.com/errors/invalid-parameter", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Invalid Request ## Code[​](#code "Direct link to Code") * `code`: `invalid_request` * `status`: `400` * `url`: `https://developers.glean.com/errors/invalid-request` ## What It Means[​](#what-it-means "Direct link to What It Means") The request could not be processed because the payload, parameters, or request structure is invalid. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The JSON body is malformed or does not match the endpoint schema. * Query parameters or request headers use unsupported values. * The request combines fields that cannot be used together. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Compare the request against the endpoint documentation and schema. * Check the response `detail` field for request-specific guidance. * Remove unsupported fields or combinations before retrying. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retrying the same request will not help. Retry only after changing the request. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/invalid-request", "title": "Invalid Request", "status": 400, "detail": "Human-readable explanation specific to this occurrence.", "code": "invalid_request", "documentation_url": "https://developers.glean.com/errors/invalid-request", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Platform Search API](/api/platform-api/search-overview.md) --- # Method Not Allowed ## Code[​](#code "Direct link to Code") * `code`: `method_not_allowed` * `status`: `405` * `url`: `https://developers.glean.com/errors/method-not-allowed` ## What It Means[​](#what-it-means "Direct link to What It Means") The endpoint exists but does not support the HTTP method used by the request. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The request uses `GET`, `POST`, `PUT`, `PATCH`, or `DELETE` incorrectly for the endpoint. * A client or proxy rewrote the method before forwarding the request. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Use the HTTP method documented for the endpoint. * Confirm any proxy or API client preserves the intended method. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry with the supported method. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/method-not-allowed", "title": "Method Not Allowed", "status": 405, "detail": "Human-readable explanation specific to this occurrence.", "code": "method_not_allowed", "documentation_url": "https://developers.glean.com/errors/method-not-allowed", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Missing Required Field ## Code[​](#code "Direct link to Code") * `code`: `missing_required_field` * `status`: `400` * `url`: `https://developers.glean.com/errors/missing-required-field` ## What It Means[​](#what-it-means "Direct link to What It Means") The request is missing a field that the endpoint requires. ## Common Causes[​](#common-causes "Direct link to Common Causes") * A required top-level property is absent from the request body. * A nested object is present but one of its required properties is missing. * A generated client or integration is sending an incomplete object. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Add the required field identified by the response `detail` or field-level `errors`. * Regenerate or update client types if they are out of date with the API schema. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retrying without adding the missing field will continue to fail. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/missing-required-field", "title": "Missing Required Field", "status": 400, "detail": "Human-readable explanation specific to this occurrence.", "code": "missing_required_field", "documentation_url": "https://developers.glean.com/errors/missing-required-field", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Rate Limit Exceeded ## Code[​](#code "Direct link to Code") * `code`: `rate_limit_exceeded` * `status`: `429` * `url`: `https://developers.glean.com/errors/rate-limit-exceeded` ## What It Means[​](#what-it-means "Direct link to What It Means") The caller has sent too many requests in a time window. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The integration is sending bursts without throttling. * Multiple workers are sharing the same token or tenant limit. * Automatic retries are amplifying traffic. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Back off before retrying. * Add client-side throttling and jitter. * Avoid retry loops that ignore rate-limit responses. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry after waiting. Use exponential backoff with jitter. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/rate-limit-exceeded", "title": "Rate Limit Exceeded", "status": 429, "detail": "Human-readable explanation specific to this occurrence.", "code": "rate_limit_exceeded", "documentation_url": "https://developers.glean.com/errors/rate-limit-exceeded", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Rate Limits](/get-started/rate-limits.md) --- # Request Timeout ## Code[​](#code "Direct link to Code") * `code`: `request_timeout` * `status`: `408` * `url`: `https://developers.glean.com/errors/request-timeout` ## What It Means[​](#what-it-means "Direct link to What It Means") The server timed out while processing the request. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The request is too broad or expensive to complete within the timeout window. * A transient service or network delay occurred. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Narrow the request when possible. * Use pagination or smaller batches for large operations. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") A retry may succeed if the timeout was transient. Use bounded retries with backoff. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/request-timeout", "title": "Request Timeout", "status": 408, "detail": "Human-readable explanation specific to this occurrence.", "code": "request_timeout", "documentation_url": "https://developers.glean.com/errors/request-timeout", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Request Too Large ## Code[​](#code "Direct link to Code") * `code`: `request_too_large` * `status`: `413` * `url`: `https://developers.glean.com/errors/request-too-large` ## What It Means[​](#what-it-means "Direct link to What It Means") The request body is larger than the API accepts. ## Common Causes[​](#common-causes "Direct link to Common Causes") * A batch contains too many items. * A field contains unexpectedly large text or metadata. * The client is sending unnecessary data. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Reduce the payload size. * Split large batches into smaller requests. * Remove fields the endpoint does not require. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry only after reducing the request size. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/request-too-large", "title": "Request Too Large", "status": 413, "detail": "Human-readable explanation specific to this occurrence.", "code": "request_too_large", "documentation_url": "https://developers.glean.com/errors/request-too-large", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Resource Not Found ## Code[​](#code "Direct link to Code") * `code`: `resource_not_found` * `status`: `404` * `url`: `https://developers.glean.com/errors/resource-not-found` ## What It Means[​](#what-it-means "Direct link to What It Means") The requested resource or endpoint could not be found or is not visible to the caller. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The resource identifier is incorrect. * The resource exists but the caller does not have access. * The endpoint path or HTTP method does not match a supported API operation. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Verify the endpoint path, resource ID, and HTTP method. * Treat hidden or unauthorized resources as indistinguishable from missing resources unless the endpoint documentation says otherwise. ### Admin Actions[​](#admin-actions "Direct link to Admin Actions") * Confirm the resource exists and that the caller should have access to it. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry only after correcting the resource identifier, endpoint path, or permissions. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/resource-not-found", "title": "Resource Not Found", "status": 404, "detail": "Human-readable explanation specific to this occurrence.", "code": "resource_not_found", "documentation_url": "https://developers.glean.com/errors/resource-not-found", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Service Unavailable ## Code[​](#code "Direct link to Code") * `code`: `service_unavailable` * `status`: `503` * `url`: `https://developers.glean.com/errors/service-unavailable` ## What It Means[​](#what-it-means "Direct link to What It Means") The API is temporarily unable to serve the request. ## Common Causes[​](#common-causes "Direct link to Common Causes") * A transient availability issue is affecting the request. * The service is overloaded or temporarily unavailable. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Retry with backoff. * Keep retries bounded so clients do not amplify service load. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry with exponential backoff and jitter. Escalate if the issue persists. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/service-unavailable", "title": "Service Unavailable", "status": 503, "detail": "Human-readable explanation specific to this occurrence.", "code": "service_unavailable", "documentation_url": "https://developers.glean.com/errors/service-unavailable", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Spend Limit Exceeded ## Code[​](#code "Direct link to Code") * `code`: `spend_limit_exceeded` * `status`: `403` * `url`: `https://developers.glean.com/errors/spend-limit-exceeded` ## What It Means[​](#what-it-means "Direct link to What It Means") The request was rejected because a configured spend limit for the deployment has been reached. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The deployment has consumed its allotted spend for the current billing period. * High-volume or automated traffic exhausted the budget faster than expected. * The configured limit is lower than the workload requires. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Stop retrying and reduce request volume until the limit is raised or the period resets. * Check the response `detail` for which limit was reached. * Contact a Glean administrator for your deployment to review the limit. ### Admin Actions[​](#admin-actions "Direct link to Admin Actions") * Review and adjust the spend limit in the Admin Console. * Investigate which integrations or workloads consumed the budget before raising the limit. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retrying will continue to fail until the limit is raised or the spend period resets. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/spend-limit-exceeded", "title": "Spend Limit Exceeded", "status": 403, "detail": "Human-readable explanation specific to this occurrence.", "code": "spend_limit_exceeded", "documentation_url": "https://developers.glean.com/errors/spend-limit-exceeded", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Token Expired ## Code[​](#code "Direct link to Code") * `code`: `token_expired` * `status`: `401` * `url`: `https://developers.glean.com/errors/token-expired` ## What It Means[​](#what-it-means "Direct link to What It Means") The authentication token was valid but has expired. ## Common Causes[​](#common-causes "Direct link to Common Causes") * An OAuth access token exceeded its lifetime. * A cached token was reused instead of refreshed. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Refresh the token through the configured authentication flow. * Update token caching so expired tokens are not reused. ### Admin Actions[​](#admin-actions "Direct link to Admin Actions") * Confirm the integration has an active credential configuration. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry after obtaining a fresh token. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/token-expired", "title": "Token Expired", "status": 401, "detail": "Human-readable explanation specific to this occurrence.", "code": "token_expired", "documentation_url": "https://developers.glean.com/errors/token-expired", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Authentication Overview](/get-started/authentication.md) --- # Token Limit Exceeded ## Code[​](#code "Direct link to Code") * `code`: `token_limit_exceeded` * `status`: `413` * `url`: `https://developers.glean.com/errors/token-limit-exceeded` ## What It Means[​](#what-it-means "Direct link to What It Means") The request exceeded the model's token limit, which caps how much content the model can process in one operation. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The supplied input is longer than the model's context window. * A continued conversation has accumulated too much history. * Large pasted documents or transcripts were included in the input. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Shorten the input, or summarize long content before sending it. * Start a new conversation instead of continuing one with a long history. * Split large content into smaller requests and combine the results client-side. * Note that this limit counts tokens, not payload bytes, unlike `request_too_large`. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retrying the same input will continue to fail. Retry only after reducing the amount of content. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/token-limit-exceeded", "title": "Token Limit Exceeded", "status": 413, "detail": "Human-readable explanation specific to this occurrence.", "code": "token_limit_exceeded", "documentation_url": "https://developers.glean.com/errors/token-limit-exceeded", "request_id": "req_7f8a9b0c1d2e" } ``` --- # Tools Unauthorized ## Code[​](#code "Direct link to Code") * `code`: `tools_unauthorized` * `status`: `422` * `url`: `https://developers.glean.com/errors/tools-unauthorized` ## What It Means[​](#what-it-means "Direct link to What It Means") The request targets an agent that uses tools the end user has not authorized, so the run cannot start. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The agent uses a tool server the end user has never connected. * The end user previously revoked access to a connected tool. * The agent was built by someone else and depends on tools the current user has not authorized. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Read `authentication_suggestions` in the response to see which tools require authorization. * POST each `server_id` to the Client API `/tool-servers/{serverId}/auth` with a `returnUrl` to obtain an `authorizationUrl`. * Redirect the end user to that `authorizationUrl`, then start the run again once OAuth completes. ### Admin Actions[​](#admin-actions "Direct link to Admin Actions") * Confirm the required tool servers are enabled for the deployment and available to the user. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retrying before the user authorizes the tools will continue to fail. Retry once authorization completes. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/tools-unauthorized", "title": "Tools Unauthorized", "status": 422, "detail": "Human-readable explanation specific to this occurrence.", "code": "tools_unauthorized", "documentation_url": "https://developers.glean.com/errors/tools-unauthorized", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Agents API Overview](/api/platform-api/agents-overview.md) --- # Unprocessable Query ## Code[​](#code "Direct link to Code") * `code`: `unprocessable_query` * `status`: `422` * `url`: `https://developers.glean.com/errors/unprocessable-query` ## What It Means[​](#what-it-means "Direct link to What It Means") The API understood the request shape but could not process the query semantics. ## Common Causes[​](#common-causes "Direct link to Common Causes") * The query is unsupported, ambiguous, or cannot be translated for the endpoint. * Query-related options conflict with each other. ## How To Resolve[​](#how-to-resolve "Direct link to How To Resolve") ### Client Actions[​](#client-actions "Direct link to Client Actions") * Simplify or rewrite the query. * Check endpoint documentation for supported query features. ## Retry Guidance[​](#retry-guidance "Direct link to Retry Guidance") Retry only after changing the query. ## Example Response[​](#example-response "Direct link to Example Response") ``` { "type": "https://developers.glean.com/errors/unprocessable-query", "title": "Unprocessable Query", "status": 422, "detail": "Human-readable explanation specific to this occurrence.", "code": "unprocessable_query", "documentation_url": "https://developers.glean.com/errors/unprocessable-query", "request_id": "req_7f8a9b0c1d2e" } ``` ## Related Docs[​](#related-docs "Direct link to Related Docs") * [Platform Search API](/api/platform-api/search-overview.md) --- # Experimental APIs Glean ships some capabilities as **experimental** so you can preview and give feedback on new functionality before it becomes generally available (GA). Experimental features are opt-in, and they may change — or be removed — without the notice period that applies to stable APIs. warning Experimental features may change in backwards-incompatible ways, or be removed entirely, **without notice**. They are not covered by our [deprecation policy](/deprecations/overview.md). Don't depend on them in production. *** ## What "Experimental" Means[​](#what-experimental-means "Direct link to What \"Experimental\" Means") ### Experimental Preview functionality that is still evolving: * Not yet generally available * May change or be removed without notice * Not covered by the deprecation policy * Opt-in only — hidden unless you ask for it ### Generally Available Stable, production-ready functionality: * Available by default * Backwards-compatible changes only * Covered by our [deprecation policy](/deprecations/overview.md) * Predictable, announced removals *** ## Opting In[​](#opting-in "Direct link to Opting In") Experimental endpoints and fields are hidden by default. To use them, send the `X-Glean-Include-Experimental` request header set to `true`: ``` X-Glean-Include-Experimental: true ``` | Header | Value | Description | | ------------------------------ | ------ | ----------------------------------------------------------------------- | | `X-Glean-Include-Experimental` | `true` | Enables experimental API features that are not yet generally available. | tip Only send this header in development or testing environments while you evaluate experimental features. Leaving it off in production keeps your integration on stable, GA behavior. ### Example Request[​](#example-request "Direct link to Example Request") ``` curl -X POST https://your-instance-be.glean.com/api/agents/search \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "X-Glean-Include-Experimental: true" \ -d '{ "name": "HR Policy Agent" }' ``` Without the `X-Glean-Include-Experimental` header, experimental endpoints and fields are not exposed, and your requests continue to run against stable API behavior only. *** ## Using the SDKs[​](#using-the-sdks "Direct link to Using the SDKs") Glean's official SDKs can set this header for you through a constructor option or an environment variable — no need to manage the raw header yourself. #### Using an Environment Variable[​](#using-an-environment-variable "Direct link to Using an Environment Variable") ``` // Set before initializing the SDK process.env.X_GLEAN_INCLUDE_EXPERIMENTAL = 'true'; import { Glean } from '@gleanwork/api-client'; const glean = new Glean({ apiToken: process.env['GLEAN_API_TOKEN'] ?? '', serverURL: 'https://your-instance-be.glean.com', }); ``` #### Using a Constructor Option[​](#using-a-constructor-option "Direct link to Using a Constructor Option") ``` import { Glean } from '@gleanwork/api-client'; const glean = new Glean({ apiToken: process.env['GLEAN_API_TOKEN'] ?? '', serverURL: 'https://your-instance-be.glean.com', includeExperimental: true, }); ``` ### Option Reference[​](#option-reference "Direct link to Option Reference") | Option | Environment Variable | Type | Description | | --------------------- | ------------------------------ | --------- | ---------------------------------------------------------------------------------------- | | `includeExperimental` | `X_GLEAN_INCLUDE_EXPERIMENTAL` | `boolean` | When `true`, sends `X-Glean-Include-Experimental: true` to enable experimental features. | note Environment variables take precedence over SDK constructor options when both are set. For language-specific details, see the "Experimental Features and Deprecation Testing" section in your SDK's documentation: * [TypeScript](https://github.com/gleanwork/api-client-typescript#experimental-features-and-deprecation-testing) * [Python](https://github.com/gleanwork/api-client-python#experimental-features-and-deprecation-testing) * [Go](https://github.com/gleanwork/api-client-go#experimental-features-and-deprecation-testing) * [Java](https://github.com/gleanwork/api-client-java#experimental-features-and-deprecation-testing) *** ## From Experimental to Generally Available[​](#from-experimental-to-generally-available "Direct link to From Experimental to Generally Available") Experimental features follow a simple lifecycle: 1 ### Experimental The feature is available behind the `X-Glean-Include-Experimental` opt-in. It may change or be removed at any time as we iterate. 2 ### Feedback & Iteration We refine the feature based on real usage and your feedback. Shapes, fields, and behavior may still change during this phase. 3 ### Generally Available The feature graduates to GA: it becomes available by default (no header required), the **Experimental** tag is removed, and it is covered by our [deprecation policy](/deprecations/overview.md) going forward. Give Feedback Experimental features are the best time to influence a feature's final shape. Share feedback with your Glean contact so we can incorporate it before general availability. --- # Authentication Overview Glean's three REST API families use different authentication models. Start by choosing the API family you need, then choose the access model that fits your integration. Follow that API's implementation guide for the exact credential and scope requirements. ## Finding Your Server URL[​](#finding-your-server-url "Direct link to Finding Your Server URL") All Glean API requests require your organization's **server URL**. That is the complete backend origin, for example `https://acme-be.glean.com`. It is not the hostname you use for the Glean web app, which is often `app.glean.com`. If automatic discovery is unavailable, ask a Glean admin for the complete **Server instance (QE)** value, including `https://`. Glean admins can copy it from the [About Glean page](https://app.glean.com/admin/about-glean). The **instance name** is only the slug inside a Glean-hosted URL (`acme` in `https://acme-be.glean.com`); most API clients and the API Explorer require the full server URL. The same API URL is used for each API family: * Platform API: `https://instance-name-be.glean.com/api/...` * Client API: `https://instance-name-be.glean.com/rest/api/v1/...` * Indexing API: `https://instance-name-be.glean.com/api/index/v1/...` ## API authentication at a glance[​](#api-authentication-at-a-glance "Direct link to API authentication at a glance") ### Platform API Supported APIs:Platform API **Recommended for new integrations** * OAuth is preferred for per-user integrations * Glean OAuth Authorization Server (on by default; some IdP OAuth tenants keep it off) or external IdP OAuth * Glean-issued tokens for Indexing, global `X-Glean-ActAs`, or when no OAuth path exists * [Platform authentication guide](/api/platform-api/authentication.md) ### Client API Supported APIs:Client API **For existing and specialized user-facing integrations** * OAuth is preferred for per-user integrations * Glean OAuth Authorization Server (on by default; some IdP OAuth tenants keep it off) or external IdP OAuth * Glean-issued tokens for global `X-Glean-ActAs`, or when no OAuth path exists * [Client authentication guide](/api-info/client/authentication/overview.md) ### Indexing API Supported APIs:Indexing API **For content ingestion and management** * Glean-issued tokens only * Datasources, documents, people, and permissions * [Indexing authentication guide](/api-info/indexing/authentication/overview.md) ## Choose an authentication path[​](#choose-an-authentication-path "Direct link to Choose an authentication path") ### Platform API: per-user or admin-managed access[​](#platform-api-per-user-or-admin-managed-access "Direct link to Platform API: per-user or admin-managed access") For per-user Platform API integrations, prefer OAuth. The Glean OAuth Authorization Server is on by default. Tenants that already used IdP OAuth may have it off. Use an external-IdP OAuth token when your organization already issues those tokens. Use a Glean-issued token for global permissions with `X-Glean-ActAs`, or when no OAuth path exists. See the [Platform API authentication guide](/api/platform-api/authentication.md). ### Client API: per-user or admin-managed access[​](#client-api-per-user-or-admin-managed-access "Direct link to Client API: per-user or admin-managed access") For per-user Client API integrations, prefer OAuth. The Glean OAuth Authorization Server is on by default. Tenants that already used IdP OAuth may have it off. Use external-IdP OAuth when your organization already issues the access tokens. Use a Glean-issued token for global permissions with `X-Glean-ActAs`, or when no OAuth path exists. See the [Client API authentication guide](/api-info/client/authentication/overview.md). ### Indexing API[​](#indexing-api "Direct link to Indexing API") The Indexing API requires Glean-issued tokens. OAuth is not supported for ordinary Indexing API operations. See the [Indexing API authentication guide](/api-info/indexing/authentication/overview.md). ## Try a request in your tenant[​](#try-a-request-in-your-tenant "Direct link to Try a request in your tenant") Use the [Search API Explorer](/api/client-api/search/search.md) to test Client API authentication against your own Glean tenant. For Platform or Indexing API authentication, use that API's getting-started guide. API Explorer is for interactive tenant testing. It is not production sample code, and it does not replace your application's OAuth flow. ## Shared security practices[​](#shared-security-practices "Direct link to Shared security practices") * Store credentials securely and never commit them to source control. * Use environment variables or a secrets manager for application credentials. * Use the least-privileged credential and scopes available for your integration. * Rotate long-lived credentials and plan for expiry before production rollout. * Handle `401`, `403`, and rate-limit responses explicitly. ## Next steps[​](#next-steps "Direct link to Next steps") 1. Choose the right API from the [Glean API overview](/api.md). 2. Follow the API-specific authentication guide above. 3. Try the API's getting-started example before building the complete integration. --- # Key Terms As you read our documentation, you may encounter terminology that is specific to Glean or has a particular meaning within the platform. Below are key definitions. ### [Core Concepts](#core-concepts) [Essential Glean platform concepts](#core-concepts) ### [Authentication & Tokens](#authentication--tokens) [API authentication and token types](#authentication--tokens) ### [Data & Content](#data--content) [Content sources and organization](#data--content) ### [AI & Search Features](#ai--search-features) [AI-powered capabilities](#ai--search-features) ### [Platform Features](#platform-features) [Built-in platform functionality](#platform-features) ### [Development Tools](#development-tools) [APIs, SDKs, and integrations](#development-tools) *** ## Core Concepts[​](#core-concepts "Direct link to Core Concepts") **Agent** – A Glean Agent is an AI program that performs tasks on behalf of users. Built using a visual interface or natural language prompts, agents require no coding. They can be conversational (e.g., chatbots answering questions) or task-based (e.g., automating workflows like IT ticket resolution or customer responses). **Client API** – A RESTful API that enables developers to interact with Glean programmatically. It's used to build custom search and chat interfaces, integrate Glean into other tools, and let AI apps query enterprise knowledge using Glean's graph. **Connector** – A connector links Glean to enterprise data sources (e.g., Slack, Google Drive, Outlook). It ingests content and permissions so information can be indexed, searched, and surfaced through Glean's AI-powered tools. **Indexing API** – A RESTful API that allows organizations to push custom content into Glean's search index, making it accessible for search and generative AI applications. **Web SDK** – A set of JavaScript libraries and UI components for embedding Glean's search and chat directly into websites or internal tools. It allows fast, customizable integration of core Glean functionality. **MCP Server** – An implementation of the Model Context Protocol that provides tools and resources to MCP hosts. Can be local (STDIO) or remote (Streamable HTTP). **Local MCP Server** – An MCP server that runs locally using STDIO communication, typically self-hosted on your own infrastructure. **Remote MCP Server** – An MCP server that runs remotely and communicates via Streamable HTTP, often cloud-hosted. **MCP Host** – An application that connects to and manages MCP servers, coordinating tool execution and resource access. **MCP Client** – The component within an MCP host that handles the actual communication protocol with MCP servers. *** ## Authentication & Tokens[​](#authentication--tokens "Direct link to Authentication & Tokens") **API Token** – Credentials for authenticating API requests. Glean provides Client API tokens (for user-facing APIs) and Indexing API tokens (for content management). **User-Scoped Token** – A token tied to a specific user's permissions and data access. **Global Token** – An administrative token that can impersonate any user (Super Admin only). *** ## Data & Content[​](#data--content "Direct link to Data & Content") **Datasource** – A configured content source in Glean (e.g., Confluence, Slack, custom integrations). **Index** – Glean's searchable content repository where all data must be added before search/AI can access it. **Collection** – A curated group of documents organized around specific topics or purposes. *** ## AI & Search Features[​](#ai--search-features "Direct link to AI & Search Features") **Chat** – Conversational AI interface for asking questions and getting insights from your enterprise data. **Summarization** – AI-powered feature that creates concise summaries of documents or search results. **Answer** – Structured Q\&A entries that provide authoritative responses to common questions. *** ## Platform Features[​](#platform-features "Direct link to Platform Features") **Announcement** – Company-wide communications distributed through Glean. **Shortcut (GoLinks)** – Short, memorable links that redirect to frequently accessed resources. **Pin** – Featured content that appears prominently in search results and recommendations. **Verification** – Process to ensure content accuracy and relevance over time. **Insight** – Analytics and metrics about content usage and search patterns. *** ## Development Tools[​](#development-tools "Direct link to Development Tools") **API Client** – Auto-generated libraries available in Python, TypeScript, Go, and Java that provide language-specific interfaces for making HTTP requests to Glean's APIs. **SDK** – Higher-level libraries that provide components, UI elements, and abstractions for building applications, beyond basic API request functionality. **MCP Tool** – In the context of Model Context Protocol, a specific function or capability that can be invoked by MCP hosts (e.g., search, chat, document retrieval). *** ## Getting Help[​](#getting-help "Direct link to Getting Help") ### [Documentation](/index.md) [Browse our complete documentation](/index.md) ### [API Reference](/api.md) [Detailed API specifications](/api.md) ### [Support](https://support.glean.com) [Get help from our support team](https://support.glean.com) --- # Rate Limits and Retries To ensure performance and stability, Glean enforces rate limits at the deployment, user, and endpoint levels. ### Deployment Rate Limits[​](#deployment-rate-limits "Direct link to Deployment Rate Limits") Applied across all incoming API traffic: * **8,000 requests per minute** across all IP addresses * **100 requests per second** from a single IP address ### User Rate Limit[​](#user-rate-limit "Direct link to User Rate Limit") * **30 requests per second** per user-scoped API token note Global tokens with an [X-Glean-ActAs](/api-info/client/authentication/glean-issued.md#authentication-headers) header are counted towards the impersonated users's quota and not the token creator's. ### Endpoint Rate Limits[​](#endpoint-rate-limits "Direct link to Endpoint Rate Limits") Rate limits for specific endpoints per user-scoped API token are as follows: | Endpoint | Rate Limit | | ------------ | ----------------------- | | /agents/runs | 0.5 requests per second | info 30 qpm is the limit for initiating agent runs. Depending on your deployment size and depending on how long-running and compute intensive the agent is, agent performance may degrade if run at max qpm. | Endpoint | Rate Limit | | -------------------- | --------------------------------------- | | /autocomplete | 10 requests per second | | /chat | 0.5 requests per second | | /feed | 7 requests per second | | /indexdocument | 10 requests per second | | /indexdocuments | 600 documents per minute | | /people | 5 requests per second | | /processalldocuments | 1 request every 3 hours per data source | | /recommendations | 0.5 requests per second | | /search | 5 requests per second | | /summarize | 0.5 requests per second | | rest of endpoints | 30 requests per second | Glean uses token bucket rate limiters for user and endpoint-level enforcement. This allows for short bursts of activity while maintaining consistent request throughput over time. ### Handling Rate Limits[​](#handling-rate-limits "Direct link to Handling Rate Limits") If you send too many requests in a short period, the API will respond with an HTTP 429 "Too Many Requests" error. To handle this gracefully, watch for 429 status codes and implement a retry mechanism with exponential backoff to gradually reduce request volume. For applications that need to make many requests, consider using a global token with the [X-Glean-ActAs](/api-info/client/authentication/glean-issued.md#authentication-headers) header to distribute requests across different users' rate limit quotas. Since requests with the ActAs header are counted towards the impersonated user's quota rather than the token creator's, this approach can help avoid hitting rate limits for a single user. If your application requires higher rate limits, please contact your Glean account team to see if the rate limits can be adjusted. --- # Direct API Integration Build custom agents using Glean's REST APIs with our official client libraries. This approach gives you full control over the agent logic while leveraging Glean's search and chat capabilities. The examples below retrieve documents via `glean.search.query()` — the **Platform API**'s data-first retrieval method (`glean.search`, not `glean.client.search`), which is what document retrieval for an agent's own context actually needs: plain results and pagination, no UI-shaped facets. It's **Experimental** as of its 2026-07 launch, so set `X_GLEAN_INCLUDE_EXPERIMENTAL=true` or every retrieval call fails. Chat and summarization below still use the Client API (`glean.client.*`) — that surface isn't changing here. ## When to Use[​](#when-to-use "Direct link to When to Use") * Custom business logic requirements * Integration with existing systems * Fine-grained control over agent behavior * Multi-language support needs ## Available Client Libraries[​](#available-client-libraries "Direct link to Available Client Libraries") ### [Python SDK](https://github.com/gleanwork/api-client-python) ``` pip install glean-api-client ``` ### [TypeScript SDK](https://github.com/gleanwork/api-client-typescript) ``` npm install @gleanwork/api-client ``` ### [Go SDK](https://github.com/gleanwork/api-client-go) ``` go get github.com/gleanwork/api-client-go ``` ### [Java SDK](https://github.com/gleanwork/api-client-java) ``` com.glean.api-client glean-api-client 0.x.x ``` ## Key APIs for Agent Building[​](#key-apis-for-agent-building "Direct link to Key APIs for Agent Building") | API | Purpose | Use Case | | -------------------------------------------------------------- | ----------------------------------- | ----------------------------------- | | **[Agents API](/api/client-api/agents/overview.md)** | Manage and execute pre-built agents | Use agents created in Agent Builder | | **[Chat API](/api/client-api/chat/overview.md)** | Conversational AI with context | Build chat-based agents | | **[Search API](/guides/search/filtering-results.md)** | Enterprise search capabilities | Retrieve relevant documents | | **[Summarization API](/api/client-api/summarize/overview.md)** | AI-powered content summarization | Process and digest information | ## Example: Python Agent[​](#example-python-agent "Direct link to Example: Python Agent") * Basic Agent * Advanced Agent ``` import os from glean.api_client import Glean, models class CustomAgent: def __init__(self): self.api_token = os.getenv("GLEAN_API_TOKEN") self.server_url = os.getenv("GLEAN_SERVER_URL") if not self.api_token or not self.server_url: raise ValueError("GLEAN_API_TOKEN and GLEAN_SERVER_URL must be set") def search_documents(self, query: str, page_size: int = 5): """Search for relevant documents via the Platform API's data-first retrieval method (requires X_GLEAN_INCLUDE_EXPERIMENTAL=true).""" with Glean(api_token=self.api_token, server_url=self.server_url) as glean: try: response = glean.search.query( query=query, page_size=page_size ) return response.results or [] except Exception as e: print(f"Search error: {e}") return [] def chat_with_context(self, query: str, search_results=None) -> str: """Generate response using chat API""" with Glean(api_token=self.api_token, server_url=self.server_url) as glean: try: # Prepare context from search results. Each result's # snippets is a plain list[str] on the Platform API -- no # nested .text/.snippet objects to unwrap. context = "" if search_results: context = "\n".join([ f"- {result.title}: {' '.join(result.snippets or [])}" for result in search_results[:3] ]) # Prepare the message with context message_text = query if context: message_text = f"Context:\n{context}\n\nQuestion: {query}" response = glean.client.chat.create( messages=[ { "fragments": [ models.ChatMessageFragment(text=message_text) ] } ] ) # Extract response text if response.messages: last_message = response.messages[-1] if hasattr(last_message, 'fragments') and last_message.fragments: for fragment in last_message.fragments: if hasattr(fragment, 'text') and fragment.text: return fragment.text return "No response received" except Exception as e: return f"Chat error: {e}" def process_query(self, query: str) -> dict: """Process a query by searching and then chatting""" # First search for relevant documents search_results = self.search_documents(query) # Then use chat to answer based on search results answer = self.chat_with_context(query, search_results) return { "answer": answer, "sources": search_results[:3], "total_sources": len(search_results) } # Usage agent = CustomAgent() result = agent.process_query("What is our vacation policy?") print(f"Answer: {result['answer']}") print(f"Found {result['total_sources']} relevant documents") ``` ``` import os from glean.api_client import Glean, models from glean.api_client.models import PlatformFilter from typing import List, Dict, Optional class AdvancedAgent: def __init__(self): self.api_token = os.getenv("GLEAN_API_TOKEN") self.server_url = os.getenv("GLEAN_SERVER_URL") self.conversation_history = [] if not self.api_token or not self.server_url: raise ValueError("GLEAN_API_TOKEN and GLEAN_SERVER_URL must be set") def search_with_filters(self, query: str, filters: Optional[Dict[str, List[str]]] = None, page_size: int = 10) -> List: """Search with optional filters, via the Platform API's data-first retrieval method (requires X_GLEAN_INCLUDE_EXPERIMENTAL=true). `filters` maps a field name to the values to match, e.g. {"department": ["HR"]} -- each becomes a PlatformFilter (EQUALS by default; OR'd across multiple values, AND'd across fields).""" with Glean(api_token=self.api_token, server_url=self.server_url) as glean: try: platform_filters = [ PlatformFilter(field=field, values=values) for field, values in (filters or {}).items() ] response = glean.search.query( query=query, page_size=page_size, filters=platform_filters or None, ) return response.results or [] except Exception as e: print(f"Search error: {e}") return [] def chat_with_context(self, query: str, context_docs: Optional[List] = None, save_to_history: bool = True) -> str: """Chat with document context and conversation history. context_docs are the Platform API's search results (attribute access, not dict-style .get() -- title and snippets are direct fields).""" with Glean(api_token=self.api_token, server_url=self.server_url) as glean: try: # Build message with context message_parts = [] if context_docs: context_text = "\n".join([ f"- {doc.title}: {' '.join(doc.snippets or [])}" for doc in context_docs[:3] if doc.snippets ]) if context_text: message_parts.append(f"Relevant context:\n{context_text}") message_parts.append(f"Question: {query}") full_message = "\n\n".join(message_parts) # Include conversation history if available messages = [] for hist_msg in self.conversation_history[-4:]: # Last 4 messages for context messages.append(hist_msg) # Add current message current_message = { "fragments": [models.ChatMessageFragment(text=full_message)] } messages.append(current_message) response = glean.client.chat.create(messages=messages) # Extract response answer = "No response received" if response.messages: last_message = response.messages[-1] if hasattr(last_message, 'fragments') and last_message.fragments: for fragment in last_message.fragments: if hasattr(fragment, 'text') and fragment.text: answer = fragment.text break # Save to conversation history if save_to_history: self.conversation_history.append(current_message) self.conversation_history.append({ "fragments": [models.ChatMessageFragment(text=answer)] }) # Keep only last 10 messages if len(self.conversation_history) > 10: self.conversation_history = self.conversation_history[-10:] return answer except Exception as e: return f"Chat error: {e}" def summarize_documents(self, documents: List) -> str: """Summarize a list of documents (the Platform API's search results, one attribute-access .url per document, not documents[i]["url"])""" with Glean(api_token=self.api_token, server_url=self.server_url) as glean: try: document_specs = [ {"url": doc.url} for doc in documents[:5] # Limit to 5 docs if doc.url ] if not document_specs: return "No documents to summarize" response = glean.client.documents.summarize( document_specs=document_specs, preferred_summary_length=500, ) if response.summary and response.summary.text: return response.summary.text return "Could not generate summary" except Exception as e: return f"Summarization error: {e}" def process_complex_query(self, query: str, department: Optional[str] = None, include_summary: bool = False) -> Dict: """Process query with department filtering and optional summarization""" filters = {} if department: filters["department"] = [department] # Search for documents search_results = self.search_with_filters(query, filters) # Generate answer with context answer = self.chat_with_context(query, search_results) result = { "answer": answer, "sources": search_results[:5], "total_sources": len(search_results), "department_filter": department } # Add summary if requested if include_summary and search_results: result["summary"] = self.summarize_documents(search_results) return result def reset_conversation(self): """Clear conversation history""" self.conversation_history = [] # Usage agent = AdvancedAgent() # Basic query result = agent.process_complex_query( "What are the remote work guidelines?", department="HR", include_summary=True ) print(f"Answer: {result['answer']}") print(f"Summary: {result.get('summary', 'No summary available')}") print(f"Found {result['total_sources']} sources") # Follow-up question (uses conversation history) followup = agent.process_complex_query("Are there any exceptions to these guidelines?") print(f"Follow-up answer: {followup['answer']}") ``` ## Best Practices[​](#best-practices "Direct link to Best Practices") ### Error Handling[​](#error-handling "Direct link to Error Handling") ``` from glean.api_client.errors import GleanError import time def robust_agent_call(agent, query, max_retries=3): """Execute agent call with retry logic""" for attempt in range(max_retries): try: return agent.process_query(query) except GleanError as e: if hasattr(e, 'status_code') and e.status_code == 429: # Rate limited wait_time = 2 ** attempt # Exponential backoff print(f"Rate limited, waiting {wait_time} seconds...") time.sleep(wait_time) elif attempt == max_retries - 1: return {"answer": f"Error after {max_retries} attempts: {e}", "sources": [], "total_sources": 0} else: print(f"Attempt {attempt + 1} failed: {e}") except Exception as e: if attempt == max_retries - 1: return {"answer": f"Unexpected error: {e}", "sources": [], "total_sources": 0} return {"answer": "Failed to get response", "sources": [], "total_sources": 0} ``` ### Caching Results[​](#caching-results "Direct link to Caching Results") ``` import hashlib import json from datetime import datetime, timedelta class CachedAgent(CustomAgent): def __init__(self, cache_ttl_minutes=30): super().__init__() self._cache = {} self.cache_ttl = timedelta(minutes=cache_ttl_minutes) def _cache_key(self, query: str, filters: dict = None) -> str: """Generate cache key from query and filters""" cache_data = {"query": query, "filters": filters or {}} cache_string = json.dumps(cache_data, sort_keys=True) return hashlib.md5(cache_string.encode()).hexdigest() def _is_cache_valid(self, timestamp: datetime) -> bool: """Check if cache entry is still valid""" return datetime.now() - timestamp < self.cache_ttl def process_query(self, query: str, filters: dict = None) -> dict: cache_key = self._cache_key(query, filters) # Check cache if cache_key in self._cache: cached_result, timestamp = self._cache[cache_key] if self._is_cache_valid(timestamp): print("Returning cached result") return cached_result # Process query result = super().process_query(query) # Cache result self._cache[cache_key] = (result, datetime.now()) return result ``` ### Rate Limiting[​](#rate-limiting "Direct link to Rate Limiting") ``` import time from datetime import datetime, timedelta from collections import deque class RateLimitedAgent(CustomAgent): def __init__(self, requests_per_minute: int = 60): super().__init__() self.requests_per_minute = requests_per_minute self.request_times = deque() def _wait_if_needed(self): """Implement rate limiting""" now = datetime.now() # Remove requests older than 1 minute while self.request_times and now - self.request_times[0] > timedelta(minutes=1): self.request_times.popleft() # Check if we need to wait if len(self.request_times) >= self.requests_per_minute: sleep_time = 60 - (now - self.request_times[0]).total_seconds() if sleep_time > 0: print(f"Rate limit reached, waiting {sleep_time:.1f} seconds...") time.sleep(sleep_time) # Record this request self.request_times.append(now) def process_query(self, query: str) -> dict: self._wait_if_needed() return super().process_query(query) ``` ## Next Steps[​](#next-steps "Direct link to Next Steps") * Explore the [Chat API overview](/api/platform-api/chat-overview.md) for building conversational agents * Check out [Search Examples](/guides/search/filtering-results.md) for advanced search capabilities * See [Agent Examples](/guides/agents/nvidia-example.md) for real-world implementations * Review the [API Reference](/api/client-api/agents/overview.md) for complete API documentation --- # Glean LangChain Integration Glean's official LangChain integration enables you to build powerful AI agents that can search and reason over your organization's knowledge using Python and the LangChain framework. ### [langchain-glean](https://github.com/gleanwork/langchain-glean) [Official LangChain integration for Glean's search and chat capabilities](https://github.com/gleanwork/langchain-glean) ### Installation[​](#installation "Direct link to Installation") * pip * poetry * uv ``` pip install -U langchain-glean ``` ``` poetry add langchain-glean ``` ``` uv add langchain-glean ``` ### Configuration[​](#configuration "Direct link to Configuration") #### API Tokens[​](#api-tokens "Direct link to API Tokens") You'll need Glean [API credentials](/get-started/authentication.md), and specifically a [user-scoped API token](/api-info/client/authentication/glean-issued.md#available-scopes). API Tokens require the following scopes: `chat`, `search`, and `agents` (if using the Agents API). You should speak to your Glean administrator to provision these tokens. #### Configure Environment Variables[​](#configure-environment-variables "Direct link to Configure Environment Variables") Configure your Glean credentials by setting the following environment variables: ``` export GLEAN_SERVER_URL="https://your-server-id-be.glean.com" export GLEAN_API_TOKEN="your-glean-api-token" export GLEAN_ACT_AS="user@example.com" # Optional: Email to act as when making requests ``` Find your server URL at [app.glean.com/admin/about-glean](https://app.glean.com/admin/about-glean). ### Usage Examples[​](#usage-examples "Direct link to Usage Examples") #### Using the Retriever[​](#using-the-retriever "Direct link to Using the Retriever") The `GleanSearchRetriever` allows you to search and retrieve documents from Glean: ``` from langchain_glean.retrievers import GleanSearchRetriever # Initialize the retriever (will use environment variables) retriever = GleanSearchRetriever() # Search for documents documents = retriever.invoke("quarterly sales report") # Process the results for doc in documents: print(f"Title: {doc.metadata.get('title')}") print(f"URL: {doc.metadata.get('url')}") print(f"Content: {doc.page_content}") print("---") ``` #### Building an Agent with Tools[​](#building-an-agent-with-tools "Direct link to Building an Agent with Tools") The `GleanSearchTool` can be used in LangChain agents to search Glean: ``` from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from langchain.agents import AgentExecutor, create_openai_tools_agent from langchain_glean.retrievers import GleanSearchRetriever from langchain_glean.tools import GleanSearchTool # Initialize the retriever retriever = GleanSearchRetriever() # Create the tool glean_tool = GleanSearchTool( retriever=retriever, name="glean_search", description="Search for information in your organization's content using Glean." ) # Create an agent with the tool llm = ChatOpenAI(model="gpt-4o") prompt = ChatPromptTemplate.from_messages([ ("system", "You are a helpful assistant with access to Glean search."), ("user", "{input}") ]) agent = create_openai_tools_agent(llm, [glean_tool], prompt) agent_executor = AgentExecutor(agent=agent, tools=[glean_tool]) # Run the agent response = agent_executor.invoke({"input": "Find the latest quarterly report"}) print(response["output"]) ``` #### RAG with LangChain Chains[​](#rag-with-langchain-chains "Direct link to RAG with LangChain Chains") You can integrate the retriever with LangChain chains for more complex workflows: ``` from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_openai import ChatOpenAI from langchain_glean.retrievers import GleanSearchRetriever # Initialize the retriever retriever = GleanSearchRetriever() # Create a prompt template prompt = ChatPromptTemplate.from_template( """Answer the question based only on the context provided. Context: {context} Question: {question}""" ) # Initialize the language model llm = ChatOpenAI(model="gpt-4o") # Format documents function def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) # Create the chain chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) # Run the chain result = chain.invoke("What were our Q2 sales results?") print(result) ``` ### Advanced Usage[​](#advanced-usage "Direct link to Advanced Usage") #### Search Parameters[​](#search-parameters "Direct link to Search Parameters") You can customize your search by passing additional parameters: ``` # Search with additional parameters documents = retriever.invoke( "quarterly sales report", page_size=5, # Number of results to return disable_spellcheck=True, # Disable spellcheck max_snippet_size=200 # Maximum snippet size ) ``` #### Custom Retriever Configuration[​](#custom-retriever-configuration "Direct link to Custom Retriever Configuration") Configure the retriever with custom settings: ``` # Initialize with custom settings retriever = GleanSearchRetriever( server_url="https://your-server-id-be.glean.com", # Override environment variable api_token="your-api-token", # Override environment variable act_as="user@example.com", # Override environment variable page_size=10, # Default number of results max_snippet_size=300 # Default snippet size ) ``` For the complete API documentation and implementation details, visit the [GitHub repository](https://github.com/gleanwork/langchain-glean). --- # Build an Agentic AI With NVIDIA NIM and Glean ![NVIDIA + Glean](/assets/images/nvidia-glean-9fd7fec6e691945ab11b0d4d0d560dcf.png) ## Introduction[​](#introduction "Direct link to Introduction") This article shows you how to create an agent using [NVIDIA NIM™](https://www.nvidia.com/en-us/ai/) and Glean that can answer user questions about an internal company knowledge base. Some example user questions are: * "How do I request access to the company VPN?" * "What are the steps to submit an expense report?" * "What is the process for booking a conference room?" * "How do I enroll in the company's health insurance plan?" * "What are the guidelines for remote work?" * "Where can I find the employee handbook?" * "How do I update my personal information in the HR portal?" ![](/assets/images/chat_interface-4eba913529ec3521d26083e097ad088c.png) ## Source Code[​](#source-code "Direct link to Source Code") The source to this example is available on [NVIDIA's Generative AI Examples GitHub Repo](https://github.com/NVIDIA/GenerativeAIExamples/tree/main/community/chat-and-rag-glean). ## Architecture[​](#architecture "Direct link to Architecture") This application uses: * NVIDIA NIM microservices for foundational LLMs and embedding models * Glean for storing the corporate knowledge base * Glean Search API for accessing the Glean knowledge base * Chroma DB for storing cached query results and performing RAG * LangGraph for creating an agent Because both Glean and NVIDIA NIM microservices can be deployed in a private environment, you can deploy this enterprise chatbot while ensuring that all data remains securely within your control. ## Prerequisites[​](#prerequisites "Direct link to Prerequisites") * Python with the [`langchain_nvidia_ai_endpoints` module](https://pypi.org/project/langchain-nvidia-ai-endpoints/) * A Glean deployment * A [Glean API key](/api-info/client/authentication/glean-issued.md) * An [NVIDIA API Key](https://build.nvidia.com/explore/discover). ## Test connection to NIM microservices[​](#test-connection-to-nim-microservices "Direct link to Test connection to NIM microservices") Before starting with the agent, you should test the LLM and embedding NIM microservices. Perform the following steps to configure your environment and create an application to test your connectivity: 1. Set the following environment variables: ``` export GLEAN_API_KEY="YOUR GLEAN API KEY" export GLEAN_API_BASE_URL="https://your-instance-be.glean.com/rest/api/v1" export NVIDIA_API_KEY="nvapi-YOUR NVIDIA API KEY" ``` 2. Create a python file called `chatbot.py` to instantiate the LLM and embedding model: ``` import os from langchain_nvidia_ai_endpoints import ChatNVIDIA, NVIDIAEmbeddings model = ChatNVIDIA( model="meta/llama-3.3-70b-instruct", api_key=os.getenv("NVIDIA_API_KEY") ) embeddings = NVIDIAEmbeddings( model="nvidia/llama-3.2-nv-embedqa-1b-v2", api_key=os.getenv("NVIDIA_API_KEY"), truncate="NONE", ) ``` note You can update this code to use different foundational LLMs, or add the `base_url` parameter if you are using privately hosted NVIDIA NIM microservices. 3. Ask the foundational LLM a test question by modifying `chatbot.py`, adding the following lines and running it: ``` response = model.invoke("How do I request access to the company VPN?") print(response.content) ``` While the model is able to interpret your question and formulate a response, it does not have access to any information about company-specific policies. ## How the agent adds contextual information[​](#how-the-agent-adds-contextual-information "Direct link to How the agent adds contextual information") To add company-specific information, your application must consider contextual information. To do this, you can use the `glean_example/src/agent.py` file. `agent.py` uses a [LangGraph](https://www.langchain.com/langgraph) agent to go through the following phases: 1. The LLM interprets the user's question and adds any relevant context. Most free form questions can be passed directly to the Glean Search API. 2. Adds relevant context about the user and then query the Glean knowledge base using the Glean search API to get the most relevant supporting documents. 3. Embeds those supporting documents into a local vector DB. 4. Uses a retriever model to fetch the most relevant supporting document based on the user's original question. 5. Takes the most relevant supporting document and add it to the LLM by adding it to the LLM's prompt (RAG). 6. Asks the LLM to summarize the results and answer the user's question with this new relevant context. The following sections explain some core concepts of the `agent.py` application's code: ### LangGraph Agent Creation[​](#langgraph-agent-creation "Direct link to LangGraph Agent Creation") This code is responsible for creating the agent: ``` class InfoBotState(BaseModel): messages: List[Tuple[str, str]] = None glean_query_required: Optional[bool] = None glean_results: Optional[List[str]] = None db: Optional[Any] = None answer_candidate: Optional[str] = None graph = StateGraph(InfoBotState) graph.add_node("determine_user_intent", determine_user_intent) graph.add_node("call_glean", call_glean) graph.add_node("add_embeddings", add_embeddings) graph.add_node("answer_candidates", answer_candidates) graph.add_node("summarize_answer", summarize_answer) graph.add_edge(START, "determine_user_intent") graph.add_conditional_edges( "determine_user_intent", route_glean, {"call_glean": "call_glean", "summarize_answer": "summarize_answer"} ) graph.add_edge("call_glean", "add_embeddings") graph.add_edge("add_embeddings", "answer_candidates") graph.add_edge("answer_candidates", "summarize_answer") graph.add_edge("summarize_answer", END) agent = graph.compile() ``` Each node represents a function responsible for implementing one of the six steps in our process. The `InfoBotState` is a special type of dictionary that will hold all of the information the agent needs through each step of the process. ### Prompt Loading[​](#prompt-loading "Direct link to Prompt Loading") The source of each function is also available in `glean_example/src/agent.py`. For example, the implementation of `call_bot` is: ``` def summarize_answer(state: InfoBotState): """the main agent responsible for taking all the context and answering the question""" logger.info("Generate final answer") llm = PROMPT_ANSWER | model response = llm.invoke( { "messages": state.messages, "glean_search_result_documents": state.glean_results, "answer_candidate": state.answer_candidate, } ) state.messages.append(("agent", response.content)) return state ``` This function takes the LLM NIM and invokes it with a specific prompt and the information available in the agent state. The prompt tells the agent what to do, injecting the relevant information from the agent state. You can see the prompts in the file `glean_example/src/prompts.py`. For example, the `PROMPT_ANSWER` is: ``` You are the final part of an agent graph. Your job is to answer the user's question based on the information below. Include a url citation in your answer. Message History: {messages} All Supporting Documents from Glean: {glean_search_result_documents} Content from the most relevant document that you should prioritize: {answer_candidate} Answer: Citation Url: ``` ### Glean Search[​](#glean-search "Direct link to Glean Search") A main part of this agent is the step that calls the Glean Search API. This RESTful request is implemented in the file `glean_example/src/glean_utils/utils.py`: ``` def glean_search(query, api_key, base_url, **kwargs): endpoint = f"{base_url}/search" headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} payload = { "query": query, "pageSize": kwargs.get("page_size", 10), "requestOptions": {}, } # Add optional parameters if "cursor" in kwargs: payload["cursor"] = kwargs["cursor"] if "facet_filters" in kwargs: payload["requestOptions"]["facetFilters"] = kwargs["facet_filters"] if "timeout_millis" in kwargs: payload["timeoutMillis"] = kwargs["timeout_millis"] try: response = requests.post(endpoint, json=payload, headers=headers) response.raise_for_status() data = response.json() result = { "status_code": response.status_code, "request_id": data.get("requestID"), "results": data.get("results", []), "facet_results": data.get("facetResults", []), "cursor": data.get("cursor"), "has_more_results": data.get("hasMoreResults", False), "tracking_token": data.get("trackingToken"), "backend_time_millis": data.get("backendTimeMillis"), } return result except requests.exceptions.RequestException as e: raise e ``` ## Run the Agent[​](#run-the-agent "Direct link to Run the Agent") To try out the agent, you can replace the code in `chatbot.py` with the following code to load the `glean_example` source code and invoke the full agent: ``` from glean_example.src.agent import agent msg = "What's the latest on the new API project?" history = [] history.append(("user", msg)) response = agent.invoke({"messages": history}) print(response["messages"][-1][1]) ``` --- # Building agents with Glean A Glean agent is built and published in Glean, then run by people in the Glean app or by your application through the [Agents API](/api/platform-api/agents-overview.md). This page helps you pick where to define an agent and how to call it, then points you to the detailed guide for each path. ## Choose an approach[​](#choose-an-approach "Direct link to Choose an approach") ### Build in Agent Builder, then run it from code[​](#build-in-agent-builder-then-run-it-from-code "Direct link to Build in Agent Builder, then run it from code") Agent Builder in the Glean app creates and configures agents. In Auto mode you describe the outcome you want and refine and test the agent; Workflow mode builds a fixed, step-by-step process with explicit control over its execution. See the [Glean Agents documentation](https://docs.glean.com/agents/) for building and publishing. API runs use the published agent, so publish your edits before calling it. To get the agent ID, click **Share** on the agent and copy the **Agent ID** from the **API** section under **Publishing options**. The ID is also the 32-character value after `/agents/` in the agent's URL. ### Run agents with the Platform Agents API[​](#run-agents-with-the-platform-agents-api "Direct link to Run agents with the Platform Agents API") The Platform API is Glean's recommended API for new integrations. Its Agents API finds agents with [`POST /api/agents/search`](/api/platform-api/platform-agents-search.md), reads their input and output schemas, and runs them with [`POST /api/agents/{agent_id}/runs`](/api/platform-api/platform-agents-create-run.md). A run either streams server-sent events (`stream: true`) or returns the agent's final messages. For work that should outlive the HTTP request, set `execution_mode` to `DURABLE`. The create call returns the run's initial snapshot with HTTP `201` without waiting for execution; you then: * Poll [`GET /api/agents/{agent_id}/runs/{run_id}`](/api/platform-api/platform-agents-get-run.md) for its `state`. An overdue run is marked `FAILED` only when it is read; without a read, the stored run can stay `RUNNING`. * When `state` is `REQUIRES_INPUT`, read `pending_interactions`. Each entry has `type: TOOL_APPROVAL`, the tool's name and description, and the exact `arguments` it wants to use. * Send an `APPROVE` or `REJECT` decision for every pending `interaction_id`, with the `run_id` in the body, to [`POST /api/agents/{agent_id}/responses`](/api/platform-api/platform-agents-create-run-responses.md). The same run then resumes. * Stop a run with [`POST /api/agents/{agent_id}/cancellations`](/api/platform-api/platform-agents-cancel-run.md). Cancellation is cooperative; poll the run for its final state. The Client API endpoints for searching agents, getting an agent or its schemas, and running agents (`/rest/api/v1/agents/...`) are deprecated in favor of these endpoints. See the [deprecation list](/deprecations/.md) for removal dates. ### Define agents as code[​](#define-agents-as-code "Direct link to Define agents as code") The [agent specification](/guides/agents/specification.md) describes an agent as a directory of files: `spec.yaml`, `instructions.md`, and optional skills and subagents. Keep the directory in version control, zip it, and send it to the Client API [Import an agent](/api/client-api/agents/import-agent.md) endpoint to create or update the agent. ### Bring Glean into another agent framework[​](#bring-glean-into-another-agent-framework "Direct link to Bring Glean into another agent framework") If your agent runs in another host or framework, give it Glean's search, chat, and tools instead of calling a Glean agent: * [Remote MCP server](/guides/mcp/.md) connects MCP hosts, including IDEs, CLIs, desktop apps, and web apps, to Glean. * [Direct API integration](/guides/agents/direct-api.md) builds agent logic around Glean's search and chat APIs with the official client libraries. * [LangChain integration](/guides/agents/langchain.md), [Agent Toolkit](/guides/agents/toolkit.md), and the [NVIDIA NIM example](/guides/agents/nvidia-example.md) cover specific frameworks. ## Authentication and scopes[​](#authentication-and-scopes "Direct link to Authentication and scopes") The Agents API accepts OAuth access tokens and Glean-issued tokens. Prefer OAuth for per-user integrations. Use a Glean-issued token for global permissions with `X-Glean-ActAs`, or when no OAuth path exists. See [Platform API authentication](/api/platform-api/authentication.md). The Agents API endpoints require the `agents` scope. The caller must also have access to the agent, and a durable run can only be read, answered, or cancelled by the user who started it. If an agent uses tools the caller has not authorized yet, the run is rejected with `422` and `authentication_suggestions` naming each tool. Have the user authorize those tools, then retry the run. Learn by building[Agents](/cookbook.md#agents) [ collection →](/cookbook.md#agents) 1. [Triage one support issue with a Glean Agent](/cookbook/triage-support-issue.md)Beginner · \~15 min 2. [Approve an agent action from a CLI](/cookbook/human-in-the-loop-agent.md)Beginner · \~5 min 3. [On-call Copilot](/cookbook/oncall-copilot.md)Advanced · \~1.5 hr --- # Glean Agent Specification The Glean agent specification is a human-readable file system format for defining a Glean agent — its instructions, tools, knowledge sources, skills, dependencies, and subagents — as a directory of files. Use it to author and version-control Glean agents outside the UI. To create or update an agent from a specification directory, zip the directory so the archive contains one top-level agent folder, and send it to the [Import an agent](/api/client-api/agents/import-agent.md) endpoint. ## Directory layout[​](#directory-layout "Direct link to Directory layout") A typical agent directory looks like this: ``` / ├── spec.yaml ├── instructions.md ├── skills/ │ └── / │ └── SKILL.md └── subagents/ └── / ├── spec.yaml ├── instructions.md └── skills/ ``` Notes: * `spec.yaml` and `instructions.md` are required for the root agent as well as for all the subagents. * `skills/` is optional. * `subagents/` is optional. * Skills should have `SKILL.md` file. * Subagents can have skills, but cannot contain nested subagents. * Published skills and agents can be referenced with `dependencies`. * The directory names should use kebab-case. * The display name of agents / subagents shown to users is defined in `spec.yaml`. ## Required files[​](#required-files "Direct link to Required files") ### `spec.yaml`[​](#specyaml "Direct link to specyaml") `spec.yaml` is the main configuration file for the agent. Common top-level fields: | Field | Required | Description | | ------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Yes | Stable unique agent identifier: a 32-character lowercase hex string, such as a `uuid.uuid4().hex` value. Do not change it after creation. | | `name` | Yes | Human-readable agent name shown to users. At most 100 characters. | | `description` | No | Short description of what the agent does. At most 300 characters. | | `instructionFile` | No | Path to the instructions file. Defaults to `instructions.md`. The file is limited to 32,000 characters. | | `model` | No | Which agentic model the agent runs on and how hard it reasons. | | `trigger` | No | How the top-level agent is invoked. Defaults to `CHAT_MESSAGE` when omitted; declaring it explicitly is recommended. Subagents do not use it. | | `skills` | No | List of skill directory paths to include. | | `subagents` | No | List of subagent directory paths to include. | | `dependencies` | No | Published skills or agents referenced by platform ID. | | `tools` | No | Tool providers and selected tools exposed to the agent. | | `gleanSearchConfig` | No | Company-knowledge access configuration. | Example: ``` id: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 name: Travel Agent description: > What this agent does, in a sentence or two. instructionFile: instructions.md model: mode: ADVANCED trigger: type: CHAT_MESSAGE skills: - skills/lead-qualification/ - skills/deal-analysis/ subagents: - subagents/research-subagent/ dependencies: - type: skill id: f1e2d3c4b5a697887766554433221100 - type: agent id: 0f1e2d3c4b5a69788776655443322110 tools: - toolProviderId: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 customisationData: skipConfirmation: true selectedTools: - name: create-doc - name: update-doc - name: search-doc - toolProviderId: 7e9f1a3b5c7d9e1f3a5b7c9d1e3f5a7b selectedTools: - name: send-email gleanSearchConfig: datasourceInstances: - asana - confluence urls: - https://drive.google.com/file/d/document/view ``` ### `instructions.md`[​](#instructionsmd "Direct link to instructionsmd") `instructions.md` contains the agent's system prompt. Guidelines: * Use Markdown for structured workflows. * Start with a clear role statement. * Keep instructions precise and complete. * Preserve exact step order when the workflow is rigid. * Do not repeat tool descriptions unnecessarily. ## Optional configuration[​](#optional-configuration "Direct link to Optional configuration") ### Model[​](#model "Direct link to Model") The `model` block configures which agentic model the agent runs on and how hard it reasons. All keys are optional. | Key | Description | | ------------- | ------------------------------------------------------------------------- | | `name` | Agentic model set ID. Tenant-specific; omit it to use the tenant default. | | `mode` | Reasoning effort per turn. `ADVANCED` (default) or `FAST`. | | `autoUpgrade` | Boolean. Omit it by default. See the rules below. | `mode` values: * `ADVANCED` — high reasoning effort. The default when `mode` is omitted. * `FAST` — low reasoning effort. This should be used only for simple tasks. `autoUpgrade` rules: * Without `name`, omit `autoUpgrade`. The tenant controls model upgrades. * With `name`, omitting `autoUpgrade` pins the agent to that model. * Set `autoUpgrade: true` to opt a named model into upgrades. * `autoUpgrade: false` pins explicitly and requires `name`; a spec with `false` and no `name` is rejected. Example: ``` model: mode: ADVANCED ``` ### Trigger[​](#trigger "Direct link to Trigger") `trigger` controls how the top-level agent is invoked. It defaults to `CHAT_MESSAGE` when omitted; declaring it explicitly, even for chat agents, keeps the intent clear. Subagents are never triggered directly, so a `trigger` in a subagent's `spec.yaml` has no effect; leave it out. Supported `type` values: | `type` | Description | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CHAT_MESSAGE` | The user converses with the agent. | | `INPUT_FORM` | The agent receives a typed set of `trigger.inputFields` provided by the user at invocation time. When `inputFields` is empty, the agent runs without any user input. | | `CONTENT_TRIGGER` | The agent runs in response to a document event declared in `trigger.content.triggers`. | Examples: ``` # Chat trigger trigger: type: CHAT_MESSAGE ``` ``` # Input form trigger trigger: type: INPUT_FORM inputFields: - displayName: Account Name description: Target account for outreach type: TEXT - displayName: Region type: SELECT defaultValue: AMER options: - value: AMER - value: EMEA - value: APJ - displayName: Due Date type: DATE optional: true ``` #### No-input trigger[​](#no-input-trigger "Direct link to No-input trigger") An agent that runs without user-supplied input uses `INPUT_FORM` with no `inputFields`: ``` trigger: type: INPUT_FORM ``` #### Content-triggered agent[​](#content-triggered-agent "Direct link to Content-triggered agent") Content-triggered agents run in response to document events. Declare events under `trigger.content.triggers`. Entries in `triggers` are ORed; conditions within one entry are ANDed, and values within one condition are ORed. ``` trigger: type: CONTENT_TRIGGER content: triggers: - datasource: JIRA templateId: JIRA_TICKET_CREATED docType: task conditions: - fieldName: priority operator: IS_ONE_OF values: [High, Critical] - datasource: GOOGLECALENDAR templateId: GOOGLECALENDAR_EVENT_SCHEDULED docType: event timeOffset: 1800 ``` `triggers` accepts at most 20 entries. Each content trigger requires `datasource` and `templateId`. `docType` is required when the subscribed event does not specify a document type. `timeOffset` and `conditions` are optional. A `conditions` entry requires `fieldName`, `operator`, and `values` (except for `FIELD_CHANGED`). Supported operators are `IS_ONE_OF`, `CONTAINS`, `IS_NOT_ONE_OF`, and `FIELD_CHANGED`. Use `TITLE` or `CONTENT` for document title/body matching; only `IS_ONE_OF` and `CONTAINS` apply to those fields. Do not declare `trigger.schedule` for content-triggered agents. #### Scheduled execution[​](#scheduled-execution "Direct link to Scheduled execution") Chat and input-form agents can also run on a wall-clock schedule: ``` trigger: type: INPUT_FORM schedule: frequency: "0 9 * * 1-5" timezone: America/Los_Angeles startTime: "2026-03-02 09:00:00" ``` * `frequency` is a required five-field Unix cron expression. Cron descriptors such as `@daily` and six-field expressions with a seconds column are rejected. * `timezone` is a required IANA timezone name. * `startTime` is optional and uses `YYYY-MM-DD HH:MM:SS` in the specified timezone. #### `inputFields` entry schema[​](#inputfields-entry-schema "Direct link to inputfields-entry-schema") Each entry in `trigger.inputFields` accepts the following keys: | Key | Required | Type | Default | Description | | -------------- | ------------ | ------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | | `displayName` | Yes | string | — | Human-readable label, and the identifier referenced from `instructions.md` as `[[displayName]]`. Must be unique within the agent. | | `description` | No | string | — | Helper text describing the field. | | `type` | Yes | enum | — | One of `TEXT`, `SELECT`, `DATE`, or `DOCUMENT`. | | `optional` | No | boolean | `false` | When `true`, the field may be left empty. | | `defaultValue` | No | string | — | Pre-filled value. For `SELECT`, must match one of `options[*].value`. For `DATE`, use `YYYY-MM-DD`. | | `options` | For `SELECT` | list | — | List of `{ value: }` entries. Required when `type` is `SELECT`. | Supported `type` values: | `type` | Description | | ---------- | ---------------------------------------------------------------------------------------- | | `TEXT` | Free-form string. Use for open-ended values the agent reads or interpolates verbatim. | | `SELECT` | Single choice from a closed set declared via `options`. | | `DATE` | Calendar date in `YYYY-MM-DD` format. | | `DOCUMENT` | A file the agent reads on each run. The value is a Glean file ID, not the file contents. | `displayName` is also the placeholder identifier used in `instructions.md`, for example `[[Account Name]]`, and must be unique within the agent. For `SELECT`, `defaultValue` must match an option. For `DATE`, use `YYYY-MM-DD`. #### Trigger authoring rules[​](#trigger-authoring-rules "Direct link to Trigger authoring rules") * `CHAT_MESSAGE` must not declare `inputFields`. * `INPUT_FORM` with fields is an input-form agent; `INPUT_FORM` without fields is a no-input agent. * `inputFields` is valid for `INPUT_FORM` and `CONTENT_TRIGGER`. * `schedule` is valid for `CHAT_MESSAGE` and `INPUT_FORM`. * `content` is valid only for `CONTENT_TRIGGER` and requires at least one content trigger. ### Tools[​](#tools "Direct link to Tools") Tools are optional. Add them when the agent needs to perform actions outside its instructions and built-in knowledge, such as search, email, or API calls. If you declare tools, make sure every instructed external action has a corresponding tool. Each tool entry includes: * `toolProviderId`: identifier of the tool provider, such as an MCP server. Use `native` for Glean's built-in tools. * `selectedTools` : list of selected tools for the specified provider * `customisationData` : optional provider-level configuration passed to all tools in the provider #### `customisationData`[​](#customisationdata "Direct link to customisationdata") Provider-level customization is optional. Users should set `skipConfirmation: true` only when they want the provider to run without confirmation. ### `gleanSearchConfig`[​](#gleansearchconfig "Direct link to gleansearchconfig") `gleanSearchConfig` controls company-knowledge access. Supported fields: | Field | Description | | --------------------- | -------------------------------------------------------------------------------------------------------- | | `datasourceInstances` | Restrict search to specific datasource instances. | | `urls` | Restrict search to specific documents or folders. Folder URLs apply transitively to contained documents. | Valid states: | State | Meaning | | --------- | ------------------------------------------------------------ | | Omitted | No company knowledge access. | | `{}` | Unrestricted company knowledge access for the invoking user. | | Populated | Restricted to the listed datasources and/or URLs. | ### Skills[​](#skills "Direct link to Skills") A skill defines: * what the skill does * when to use it * how it works Each skill directory must contain a `SKILL.md` file with frontmatter `name` (at most 128 characters) and `description` (at most 1,000 characters). The display name is the title-cased folder name; for example, `lead-qualification` becomes "Lead Qualification". ### Dependencies[​](#dependencies "Direct link to Dependencies") Dependencies reference published skills or agents by platform ID. They are not copied into the agent directory and must not also be listed under `skills/` or `subagents/`: ``` dependencies: - type: skill id: f1e2d3c4b5a697887766554433221100 - type: agent id: 0f1e2d3c4b5a69788776655443322110 ``` Use `type: skill` for a published skill and `type: agent` for a published agent that the parent can dispatch as a reusable subagent. Inline subagents listed under `subagents` are authored as folders in the agent directory; published agent dependencies are referenced by ID instead. Dependency IDs must be unique for each dependency type. The top-level agent may declare both dependency types. A subagent may declare skill dependencies, but must not declare an agent dependency because nested subagents are not supported. ### Subagents[​](#subagents "Direct link to Subagents") Subagents are optional isolated execution units. Rules: * A subagent has its own `spec.yaml` and `instructions.md`. * A subagent may have its own `skills/` directory. * A subagent must not declare `subagents` or `trigger`; nested subagents are not supported and subagents are never invoked directly. * A subagent may reference published skills with `dependencies`. * The parent agent receives only the subagent's final result. Subagent `spec.yaml` uses the same fields as the parent except for `trigger` and `subagents`. ## Minimal example[​](#minimal-example "Direct link to Minimal example") ``` my-agent/ ├── spec.yaml ├── instructions.md └── skills/ └── domain-knowledge/ └── SKILL.md ``` This is a minimal useful agent with one skill. More advanced agents can also include tools, knowledge restrictions, and subagents. --- # Glean Agent Toolkit A Python toolkit that makes it easy to integrate Glean's powerful search and knowledge discovery capabilities into your AI agents. Use pre-built tools with popular agent frameworks or create your own custom tools that work across multiple platforms. ## When to Use[​](#when-to-use "Direct link to When to Use") * Working with multiple agent frameworks (OpenAI, LangChain, CrewAI, Google ADK) * Need production-ready Glean tools (search, employee lookup, calendar, etc.) * Want to define tools once and use across frameworks * Building agents that require enterprise knowledge access ### [glean-agent-toolkit](https://github.com/gleanwork/glean-agent-toolkit) [Official Python toolkit for adapting Glean's enterprise tools across multiple agent frameworks](https://github.com/gleanwork/glean-agent-toolkit) ## Framework Support[​](#framework-support "Direct link to Framework Support") The toolkit provides adapters for major agent frameworks: | Framework | Installation | Use Case | | --------------------- | -------------------------------------------- | ------------------------------------- | | **All frameworks** | `pip install "glean-agent-toolkit[all]"` | Full framework support (recommended) | | **OpenAI Agents SDK** | `pip install glean-agent-toolkit[openai]` | Build agents with OpenAI's Agents SDK | | **LangChain** | `pip install glean-agent-toolkit[langchain]` | LangChain / LangGraph agents | | **CrewAI** | `pip install glean-agent-toolkit[crewai]` | Multi-agent collaboration | | **Google ADK** | `pip install glean-agent-toolkit[adk]` | Google's Agent Development Kit | ## Available Tools[​](#available-tools "Direct link to Available Tools") The toolkit ships nine production-ready tools that connect to Glean's capabilities. The import name is the symbol you import from `glean.agent_toolkit.tools`; the tool name is the identifier exposed to the LLM. | Import name | Tool name | Description | | --------------------- | ----------------------- | ---------------------------------------------- | | **`search`** | `glean_search` | Search internal documents and knowledge bases | | **`chat`** | `glean_chat` | Conversational Q\&A with Glean Assistant | | **`read_document`** | `glean_read_document` | Read full document content by ID or URL | | **`web_search`** | `glean_web_search` | Search the public web for external information | | **`calendar_search`** | `glean_calendar_search` | Find meetings and calendar events | | **`employee_search`** | `glean_employee_search` | Search employees by name, team, or department | | **`code_search`** | `glean_code_search` | Search source code repositories | | **`gmail_search`** | `glean_gmail_search` | Search Gmail messages and conversations | | **`outlook_search`** | `glean_outlook_search` | Search Outlook mail and calendar items | Import the tools you need: ``` from glean.agent_toolkit.tools import search, chat, read_document from glean.agent_toolkit.tools import web_search, calendar_search from glean.agent_toolkit.tools import employee_search, code_search from glean.agent_toolkit.tools import gmail_search, outlook_search ``` The quickest way to hand every built-in tool to a framework is `get_tools()`, which returns all nine tools already converted for your framework of choice: ``` from glean.agent_toolkit import get_tools # Credentials are read from GLEAN_API_TOKEN and GLEAN_SERVER_URL. tools = get_tools("langchain") # or "openai", "crewai", "adk" ``` ## Example: Multi-Framework Usage[​](#example-multi-framework-usage "Direct link to Example: Multi-Framework Usage") ``` from glean.agent_toolkit.tools import search, employee_search import os # Ensure environment variables are set os.environ["GLEAN_API_TOKEN"] = "your-api-token" os.environ["GLEAN_SERVER_URL"] = "https://your-company-be.glean.com" # Use with LangChain langchain_search = search.as_langchain_tool() langchain_employees = employee_search.as_langchain_tool() # Use with CrewAI crewai_search = search.as_crewai_tool() crewai_employees = employee_search.as_crewai_tool() # Use with OpenAI Agents SDK openai_search = search.as_openai_tool() openai_employees = employee_search.as_openai_tool() ``` ## Custom Tool Creation[​](#custom-tool-creation "Direct link to Custom Tool Creation") Define your own tools once using the `@tool_spec` decorator and use them across any framework: ``` from glean.agent_toolkit import tool_spec from pydantic import BaseModel import requests class WeatherResponse(BaseModel): temperature: float condition: str city: str @tool_spec( name="get_current_weather", description="Get current weather information for a specified city", output_model=WeatherResponse ) def get_weather(city: str, units: str = "celsius") -> WeatherResponse: """Fetch current weather for a city.""" return WeatherResponse( temperature=22.5, condition="sunny", city=city ) # Use across all supported frameworks openai_weather = get_weather.as_openai_tool() langchain_weather = get_weather.as_langchain_tool() crewai_weather = get_weather.as_crewai_tool() ``` ## Complete Example: Multi-Agent System[​](#complete-example-multi-agent-system "Direct link to Complete Example: Multi-Agent System") ``` from glean.agent_toolkit.tools import search, employee_search, calendar_search from crewai import Agent, Task, Crew import os os.environ["GLEAN_API_TOKEN"] = "your-api-token" os.environ["GLEAN_SERVER_URL"] = "https://your-company-be.glean.com" # Create agents with different specializations researcher = Agent( role='Research Specialist', goal='Find relevant company information and documents', backstory='Expert at searching and analyzing company knowledge', tools=[search.as_crewai_tool()] ) hr_specialist = Agent( role='HR Specialist', goal='Find employee information and schedule meetings', backstory='Expert at employee relations and scheduling', tools=[ employee_search.as_crewai_tool(), calendar_search.as_crewai_tool() ] ) # Define tasks research_task = Task( description='Find information about our remote work policy', agent=researcher ) scheduling_task = Task( description='Find the HR manager and check their availability this week', agent=hr_specialist ) # Create and run the crew crew = Crew( agents=[researcher, hr_specialist], tasks=[research_task, scheduling_task] ) result = crew.kickoff() ``` ## Key Benefits[​](#key-benefits "Direct link to Key Benefits") * **Framework Agnostic**: Write tools once, use everywhere * **Production-Ready**: Pre-built tools for common Glean operations * **Easy Integration**: Simple adapter pattern for different frameworks * **Consistent API**: Same tool interface across all platforms * **Enterprise Features**: Built-in support for Glean's enterprise capabilities ## Advanced Usage[​](#advanced-usage "Direct link to Advanced Usage") ### Tool Composition[​](#tool-composition "Direct link to Tool Composition") ``` from glean.agent_toolkit.tools import search, employee_search from glean.agent_toolkit import tool_spec @tool_spec( name="research_and_contact", description="Research a topic and find relevant experts to contact" ) def research_and_contact(topic: str) -> dict: """Research a topic and find experts.""" # Calling a tool directly returns a ToolResult envelope: # {"status": "ok" | "error", "result": , "error": ..., ...} search_response = search(query=topic) search_results = search_response["result"] if search_response["status"] == "ok" else [] # Extract mentioned people from results mentioned_people = extract_people_from_results(search_results) # Find employee details experts = [] for person_name in mentioned_people: employee_response = employee_search(query=person_name) if employee_response["status"] == "ok" and employee_response["result"]: experts.append(employee_response["result"]) return { "research_results": search_results, "experts": experts, "summary": f"Found {len(search_results)} documents and {len(experts)} experts on {topic}" } def extract_people_from_results(results): """Extract people mentioned in search results.""" # Implementation to parse names from document content pass ``` ### Error Handling and Retries[​](#error-handling-and-retries "Direct link to Error Handling and Retries") ``` from glean.agent_toolkit.tools import search from glean.agent_toolkit import tool_spec import time # Built-in tools return a ToolResult envelope and never raise on API errors; # inspect result["status"] and retry on transient error types. RETRYABLE = {"timeout", "rate_limit", "api"} @tool_spec( name="robust_search", description="Search with automatic retries and error handling" ) def robust_search(query: str, max_retries: int = 3) -> dict: """Search with retry logic.""" for attempt in range(max_retries): result = search(query=query) if result["status"] == "ok": return result if result["error_type"] in RETRYABLE and attempt < max_retries - 1: time.sleep(2 ** attempt) # Exponential backoff continue return result ``` ## Next Steps[​](#next-steps "Direct link to Next Steps") * **Get Started**: Install the toolkit with `pip install glean-agent-toolkit` * **Documentation**: Visit the [GitHub repository](https://github.com/gleanwork/glean-agent-toolkit) for complete documentation --- # Simple Chatbot Example This example demonstrates how to build a simple chatbot using Glean's Chat API with the official Python client. The chatbot can answer questions using your company's knowledge base. ## Prerequisites[​](#prerequisites "Direct link to Prerequisites") * OAuth access token or Glean-issued API token with `CHAT` scope * Python 3.8+ * Your Glean instance name For a per-user chatbot, prefer OAuth. The Python client's `api_token` field accepts an OAuth access token through the same credential shape shown in this example. Use a Glean-issued token for global `X-Glean-ActAs`, or when no OAuth path exists. See [Client API authentication](/api-info/client/authentication/overview.md) before you deploy. ## Installation[​](#installation "Direct link to Installation") ``` pip install glean-api-client ``` ## Basic Chat Example[​](#basic-chat-example "Direct link to Basic Chat Example") ``` import os from glean.api_client import Glean, models def send_chat_message(message_text): """Send a message to Glean Chat and get the response.""" api_token = os.getenv("GLEAN_API_TOKEN") server_url = os.getenv("GLEAN_SERVER_URL") if not api_token: raise ValueError("GLEAN_API_TOKEN environment variable is required") if not server_url: raise ValueError("GLEAN_SERVER_URL environment variable is required") with Glean(api_token=api_token, server_url=server_url) as glean: try: response = glean.client.chat.create( messages=[ { "fragments": [ models.ChatMessageFragment( text=message_text, ), ], }, ], ) if response.messages: last_message = response.messages[-1] if hasattr(last_message, 'fragments') and last_message.fragments: for fragment in last_message.fragments: if hasattr(fragment, 'text') and fragment.text: return fragment.text return "No response received" except Exception as e: return f"Error: {str(e)}" def main(): """Simple interactive chatbot.""" print("Glean Chatbot - Type 'quit' to exit") print("-" * 40) while True: user_input = input("\nYou: ").strip() if user_input.lower() in ['quit', 'exit', 'q']: print("Goodbye!") break if not user_input: continue print("Bot: ", end="", flush=True) try: response = send_chat_message(user_input) print(response) except ValueError as e: print(f"Setup error: {e}") break if __name__ == "__main__": main() ``` ## Environment Setup[​](#environment-setup "Direct link to Environment Setup") Create a `.env` file in your project directory: ``` GLEAN_API_TOKEN=your_oauth_or_glean_token_here GLEAN_SERVER_URL=https://your-server-id-be.glean.com ``` ## Running the Example[​](#running-the-example "Direct link to Running the Example") 1. Set your environment variables: ``` export GLEAN_API_TOKEN="your_oauth_or_glean_token_here" export GLEAN_SERVER_URL="https://your-server-id-be.glean.com" ``` 2. Run the chatbot: ``` python chatbot.py ``` 3. Start asking questions: ``` You: What is our vacation policy? Bot: Based on your company's HR documentation, here are the key points about vacation policy... You: Who should I contact about benefits? Bot: For benefits questions, you should reach out to... ``` ## Streaming Chat Example[​](#streaming-chat-example "Direct link to Streaming Chat Example") For real-time streaming responses: ``` import os from glean.api_client import Glean, models def stream_chat_response(message_text): """Stream chat response in real-time.""" api_token = os.getenv("GLEAN_API_TOKEN") server_url = os.getenv("GLEAN_SERVER_URL") if not api_token: print("Error: GLEAN_API_TOKEN environment variable required") return if not server_url: print("Error: GLEAN_SERVER_URL environment variable required") return with Glean(api_token=api_token, server_url=server_url) as glean: try: response_stream = glean.client.chat.create_stream( messages=[ { "fragments": [ models.ChatMessageFragment( text=message_text, ), ], }, ], ) print("Bot: ", end="", flush=True) for chunk in response_stream: if chunk: print(chunk, end="", flush=True) print() except Exception as e: print(f"Error during streaming: {e}") def main(): """Interactive streaming chatbot.""" print("Glean Streaming Chatbot - Type 'quit' to exit") print("-" * 45) while True: user_input = input("\nYou: ").strip() if user_input.lower() in ['quit', 'exit', 'q']: print("Goodbye!") break if user_input: stream_chat_response(user_input) if __name__ == "__main__": main() ``` ## Conversation History Example[​](#conversation-history-example "Direct link to Conversation History Example") Maintain context across multiple messages: ``` import os from glean.api_client import Glean, models class ConversationalChatbot: def __init__(self): self.chat_id = None self.api_token = os.getenv("GLEAN_API_TOKEN") self.server_url = os.getenv("GLEAN_SERVER_URL") if not self.api_token: raise ValueError("GLEAN_API_TOKEN environment variable is required") if not self.server_url: raise ValueError("GLEAN_SERVER_URL environment variable is required") def send_message(self, user_message): """Send a message with conversation history.""" with Glean(api_token=self.api_token, server_url=self.server_url) as glean: try: response = glean.client.chat.create( messages=[ { "fragments": [ models.ChatMessageFragment( text=user_message, ), ], }, ], chat_id=self.chat_id, save_chat=True, ) if hasattr(response, 'chat_id') and response.chat_id: self.chat_id = response.chat_id if response.messages: last_message = response.messages[-1] if hasattr(last_message, 'fragments') and last_message.fragments: for fragment in last_message.fragments: if hasattr(fragment, 'text') and fragment.text: return fragment.text return "No response received" except Exception as e: return f"Error: {str(e)}" def reset_conversation(self): """Start a new conversation.""" self.chat_id = None def main(): """Conversational chatbot with memory.""" print("Glean Conversational Chatbot") print("Type 'quit' to exit, 'reset' to start new conversation") print("-" * 55) try: chatbot = ConversationalChatbot() except ValueError as e: print(f"Setup error: {e}") return while True: user_input = input("\nYou: ").strip() if user_input.lower() in ['quit', 'exit', 'q']: print("Goodbye!") break elif user_input.lower() == 'reset': chatbot.reset_conversation() print("Started new conversation!") continue elif not user_input: continue print("Bot: ", end="", flush=True) response = chatbot.send_message(user_input) print(response) if __name__ == "__main__": main() ``` ## Error Handling[​](#error-handling "Direct link to Error Handling") Add robust error handling for production use: ``` import logging from glean.api_client import Glean, models from glean.api_client.errors import GleanError logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def send_chat_message_with_retry(message_text, max_retries=3): """Send chat message with retry logic.""" api_token = os.getenv("GLEAN_API_TOKEN") server_url = os.getenv("GLEAN_SERVER_URL") if not api_token: return "Missing GLEAN_API_TOKEN" if not server_url: return "Missing GLEAN_SERVER_URL" for attempt in range(max_retries): try: with Glean(api_token=api_token, server_url=server_url) as glean: response = glean.client.chat.create( messages=[ { "fragments": [ models.ChatMessageFragment( text=message_text, ), ], }, ], ) if response.messages: last_message = response.messages[-1] if hasattr(last_message, 'fragments') and last_message.fragments: for fragment in last_message.fragments: if hasattr(fragment, 'text') and fragment.text: return fragment.text return "No response received" except GleanError as e: logger.warning(f"Glean API error on attempt {attempt + 1}: {e}") if attempt == max_retries - 1: return f"Failed after {max_retries} attempts: {e}" except Exception as e: logger.error(f"Unexpected error: {e}") return f"Unexpected error: {e}" return "Failed to get response" ``` ## Advanced Features[​](#advanced-features "Direct link to Advanced Features") ### Using Custom Agents[​](#using-custom-agents "Direct link to Using Custom Agents") ``` # Use a specific agent for specialized responses with Glean(api_token=api_token, server_url=server_url) as glean: response = glean.client.chat.create( messages=[ { "fragments": [ models.ChatMessageFragment( text="Help me write a technical document", ), ], }, ], agent_id="your-custom-agent-id", ) ``` ## Next Steps[​](#next-steps "Direct link to Next Steps") * **Add conversation persistence**: Use the `chat_id` to maintain long-running conversations * **Implement user authentication**: Handle different user permissions * **Add file upload support**: Use `upload_files()` for document analysis * **Deploy as a web service**: Use Flask/FastAPI to create a web interface * **Integrate with Slack/Teams**: Build a bot for your team chat platform ## Related Documentation[​](#related-documentation "Direct link to Related Documentation") * [Chat API Reference](/api/client-api/chat/overview.md) * [Authentication Guide](/get-started/authentication.md) * [Python API Client](/libraries/api-clients/python.md) --- # Deep Linked Citations This guide explains how to update a custom chat frontend that uses Glean's `/chat` REST API to take advantage of **deep-linked (direct-quote) citations**, while remaining backward compatible with existing responses. Deep-linked citations are available starting Q4 2025. REST API support is currently in Beta. ## Overview[​](#overview "Direct link to Overview") Deep-linked citations upgrade Glean Chat's citations from **document-level** to **text-level**: * Citations can now include **exact snippets** from the source document that support the preceding statement, rather than just pointing to the document as a whole. * In the `/chat` response, this appears as a new optional `referenceRanges[].snippets[]` array attached to each inline `citation` fragment. Backward Compatible If your integration ignores these new fields, everything continues to work. The change is **additive and backward compatible**. This guide is about how to opt in and render the richer experience. ## Prerequisites[​](#prerequisites "Direct link to Prerequisites") Deep-linked citations are available when all of the following are true: ### Agent Configuration Your `/chat` request must set `agentConfig.agent` to `"FAST"` or `"ADVANCED"`. If omitted, requests default to Chat V2 which does not support deep-linked citations. ### Supported LLM Models Deep-linked citations are supported with OpenAI (GPT) and Anthropic (Claude) models. Gemini is not currently supported. note Deep-linked citations are provided for answers grounded in **enterprise content** (company documents, email, etc.), not for pure world-knowledge or web search answers. If these conditions are not met, your existing citation UI continues to function as today. ## Response Format[​](#response-format "Direct link to Response Format") ### Baseline Structure (Unchanged)[​](#baseline-structure-unchanged "Direct link to Baseline Structure (Unchanged)") Each chat response message still looks like: ``` { "messages": [ { "author": "GLEAN_AI", "fragments": [ { "text": "Some generated text. " }, { "citation": { "sourceDocument": { "id": "DOC_ID", "datasource": "gdrive", "title": "Document title", "url": "https://docs.google.com/..." } } } ], "citations": [ /* existing top-level citations, unchanged */ ] } ] } ``` Your existing integration likely uses: * `fragments[].text` to render the assistant answer * `fragments[].citation.sourceDocument` and/or `citations[]` to render citation pills All of that remains valid. ### New `referenceRanges` Field[​](#new-referenceranges-field "Direct link to new-referenceranges-field") When deep-linked citations are available for a given citation, the fragment's `citation` now also includes **reference ranges** with **snippets**: ``` { "messages": [ { // ... "fragments": [ // ... { "citation": { "sourceDocument": { /* ... */ }, "referenceRanges": [ { "textRange": { "startIndex": 50, "endIndex": 120, "type": "LINK" }, "snippets": [ { "text": "I'd like to try out Codex for engineering tasks...", "pageNumber": 1 }, { "text": "to the ChatGPT Teams account?" } ] } ] } } ], "citations": [ /* ... */ ] } ] } ``` **Key points:** | Field | Description | | ----------------------------------------- | ----------------------------------------------------------------------------------------------- | | `referenceRanges[]` | Optional array. Many citations will continue to be document-level only. | | `referenceRanges[].textRange` | Describes which part of the assistant response the citation is attached to (existing behavior). | | `referenceRanges[].snippets[]` | **New**: An array of direct-quote snippets from the source document that support the statement. | | `referenceRanges[].snippets[].text` | The quoted text from the source document. | | `referenceRanges[].snippets[].pageNumber` | Optional page number where the snippet appears. | Each `snippet` is a `SearchResultSnippet` object from the public search/documents API, shared with other Glean surfaces. At minimum it has `text: string`, but may also include `pageNumber: number` and other metadata such as snippet identifiers. note Top-level `citations[]` on the message remain unchanged and still hold document-level info. They are not required to implement deep-linking but may be useful for your existing UI logic. ## Migration Options[​](#migration-options "Direct link to Migration Options") ### Minimal Migration Stay compatible without UI changes—just update your types to tolerate the new fields ### Full Migration Render deep-linked citations with hover previews showing direct quotes ### Minimal Migration[​](#minimal-migration "Direct link to Minimal Migration") If you only want to stay compatible without changing your UI: 1. **Update your deserialization model** to tolerate the new fields: * The `citation` object now may contain a `referenceRanges` array with nested `snippets` * If you are using a generated client or strict type definitions, regenerate from the latest OpenAPI spec so these fields are known but optional 2. **Ignore `referenceRanges` / `snippets` for now.** The behavior will match pre-deep-linked citations. This is a good first step while you plan UI updates. ### Full Migration[​](#full-migration "Direct link to Full Migration") If your goal is to mirror the Glean UI behavior, you'll render hover cards that show the **direct quote** backing each statement, plus surrounding context and (where available) a **page number** and preview. **High-level flow for each assistant message:** 1. Render `fragments[].text` as you do today 2. For each `fragments[].citation`: * Always show the document-level citation pill (title, datasource, icon, etc.) based on `sourceDocument`—this is unchanged * If `referenceRanges[].snippets[]` is present, use those snippets to populate the **hover card / popover** with direct quotes and surrounding context * Optionally show the `pageNumber` when present 3. Clicking the citation can continue to open `sourceDocument.url` ## Fetching Document Content[​](#fetching-document-content "Direct link to Fetching Document Content") To display **surrounding text** around each snippet, fetch the full document text via `/getdocuments` using the cited document IDs: ``` POST /rest/api/v1/getdocuments { "documentSpecs": [{ "id": "DOC_ID" }], "includeFields": ["DOCUMENT_CONTENT"] } ``` The response includes `content.fullTextList` which you can join to get the full document text, then search for each snippet's `text` within that string to derive preceding and following context. ## Code Examples[​](#code-examples "Direct link to Code Examples") * TypeScript/React * Pseudocode ``` type Snippet = { text: string; pageNumber?: number } async function loadDocumentContent(docId: string): Promise { const resp = await fetch("/rest/api/v1/getdocuments", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ documentSpecs: [{ id: docId }], includeFields: ["DOCUMENT_CONTENT"], }), }) const data = await resp.json() const doc = Object.values(data.documents)[0] as any return (doc?.content?.fullTextList || []).join("\n\n") } function findSurroundingText( fullText: string, snippetText: string, windowChars = 200, ): { preceding: string; cited: string; following: string } { const idx = fullText.indexOf(snippetText) if (idx === -1) { return { preceding: "", cited: snippetText, following: "" } } const start = Math.max(0, idx - windowChars) const end = Math.min(fullText.length, idx + snippetText.length + windowChars) return { preceding: fullText.slice(start, idx), cited: fullText.slice(idx, idx + snippetText.length), following: fullText.slice(idx + snippetText.length, end), } } function CitationPopover({ citation }: { citation: any }) { const [docContent, setDocContent] = useState(null) useEffect(() => { if (citation.sourceDocument?.id) { loadDocumentContent(citation.sourceDocument.id).then(setDocContent) } }, [citation.sourceDocument?.id]) const ranges = citation.referenceRanges ?? [] if (!ranges.length) { // Fallback: document-level citation only return
{citation.sourceDocument?.title}
} return (
{citation.sourceDocument?.title} {ranges[0]?.snippets?.[0]?.pageNumber != null && ( · Page {ranges[0].snippets[0].pageNumber} )}
{ranges.flatMap((range, idx) => (range.snippets || []).map((s, j) => { if (!docContent) { return
{s.text}
} const { preceding, cited, following } = findSurroundingText( docContent, s.text, ) // Fallback if snippet text couldn't be matched in document if (!preceding && !following) { return
{s.text}
} return (

{preceding && ...{preceding}} {cited} {following && {following}...}

) }), )}
) } ``` ``` # For each citation with referenceRanges: 1. Extract document ID from citation.sourceDocument.id 2. Fetch document content: POST /getdocuments Body: { documentSpecs: [{ id: docId }], includeFields: ["DOCUMENT_CONTENT"] } 3. Join fullTextList to get complete document text 4. For each snippet in referenceRanges[].snippets[]: a. Find snippet.text within the full document text b. Extract ~200 chars before and after for context c. Render with: - Preceding context (muted/gray) - Snippet text (highlighted) - Following context (muted/gray) - Page number if available 5. Show in hover popover when user hovers citation pill ``` ## UX Guidelines[​](#ux-guidelines "Direct link to UX Guidelines") ### When Users See Deep-Linked Snippets[​](#when-users-see-deep-linked-snippets "Direct link to When Users See Deep-Linked Snippets") * Not every citation pill will have snippet highlights—deep-linking only appears when the LLM confidently matches a snippet * When present, the hover popover should: * Highlight the **cited snippet** with emphasis (e.g., background color or bold) * Show a few lines before and after for context * Optionally show **page number** if provided * Optionally offer an **"Expand"** button for a larger preview ### Limitations[​](#limitations "Direct link to Limitations") | Limitation | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Complex formatting** | Tables, spreadsheets, code blocks, and heavy HTML may render as plain text in the snippet preview, which can look noisy or hard to read | | **Source navigation** | Clicking a citation opens the underlying document. Jumping directly to the exact page/offset is not guaranteed for all sources—it's supported only in some cases (e.g., slide number for certain PPT sources, some page-number support for PDFs) and is still being expanded | | **Coverage** | Deep-linked citations are not provided for world-knowledge or web-search-only answers. Some citations may intentionally fall back to document-level when matching is uncertain, to avoid misleading users | Design your UI to gracefully fall back to document-level behavior when no `referenceRanges` / `snippets` are present. ## Migration Checklist[​](#migration-checklist "Direct link to Migration Checklist") 1 ### Verify Server Configuration Ensure your `/chat` requests set `agentConfig.agent` to `FAST` or `ADVANCED` and use an LLM configuration that supports deep-linked citations. 2 ### Update API Types Regenerate your client types from the latest OpenAPI spec so `ChatMessageCitation.referenceRanges[].snippets[]` and `SearchResultSnippet.pageNumber` are present but optional. 3 ### Handle New Fields Defensively Ensure your deserialization tolerates the new fields. Handle unknown/extra fields defensively to accommodate future enhancements. 4 ### Update Citation UI Keep your existing document-level citation pills (title, icon, click → `sourceDocument.url`). If `referenceRanges[].snippets[]` exists, show snippet text in your hover/detail view with optional page number. 5 ### Add Contextual Preview (Recommended) On hover or "expand", call `/getdocuments` with the cited document IDs and `includeFields: ["DOCUMENT_CONTENT"]`. Use the returned `fullTextList` to compute surrounding context and highlight each snippet. ## Related Documentation[​](#related-documentation "Direct link to Related Documentation") ### [Chat API Reference](/api/client-api/chat/overview.md) [Complete reference for the /chat endpoint](/api/client-api/chat/overview.md) ### [Read Documents API](/api/client-api/documents/getdocuments.md) [Fetch document content with includeFields options](/api/client-api/documents/getdocuments.md) --- # Remote MCP Server Connect an MCP host to your organization's Glean knowledge graph and the tools your administrator has enabled. The Remote MCP Server is tenant-specific and permission-aware. Each person uses their own Glean access. note This is different from the public [Docs MCP Server](/docs-mcp.md), which gives a coding assistant access to Glean developer documentation. Use the Remote MCP Server for your organization's indexed knowledge and configured tools. ## Check that you can connect[​](#check-that-you-can-connect "Direct link to Check that you can connect") Glean enables the OAuth Authorization Server and Remote MCP Server by default for eligible tenants. Administrators can disable MCP or hide the end-user Configurator. Open the Configurator first. If it is unavailable, ask an administrator to confirm that at least one MCP server is enabled and that the Configurator is visible. 1 Open the MCP Configurator Open the [MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true), choose the server you want to connect, and copy its URL. Selecting a host is optional; do it when you want setup instructions tailored to that application. See the [MCP Configurator user guide](https://docs.glean.com/user-guide/mcp/usage) if the navigation in Glean differs. 2 If the Configurator is missing If the Configurator is unavailable, ask a Glean administrator to confirm that at least one MCP server is enabled and that the end-user Configurator is visible. That check lives in [Set up Glean MCP server](https://docs.glean.com/administration/platform/mcp/enable-mcp-servers). 3 Match the host to its setup path Check [Supported MCP Hosts](/guides/mcp/supported-hosts.md) to see whether your host is user-configurable or organization-managed. One server URL connects any MCP host, including hosts the Configurator does not list. For an unlisted host, use that URL with the host's own MCP setup instructions. ## Connect with OAuth[​](#connect-with-oauth "Direct link to Connect with OAuth") Complete the host's OAuth sign-in flow with your organization's SSO when prompted. Dynamic Client Registration (DCR) is the usual path for MCP hosts. A tenant can allow any DCR-capable application, restrict registration to approved applications, or turn DCR off. Glean provisions new and previously unconfigured default-on MCP tenants with the Glean-managed list of approved applications; tenants with existing MCP or OAuth configuration keep their settings. If DCR is disabled, your tenant does not allow the host to register, or the host needs scopes DCR does not grant, an administrator can register a [static OAuth client](https://docs.glean.com/administration/oauth/static-client-registration). See [OAuth authentication](/api-info/client/authentication/oauth.md) for the DCR versus static model. Use a Glean-issued API token only when the host cannot complete OAuth. Do not assign API Token Creator in bulk as an MCP rollout. ## Verify the connection[​](#verify-the-connection "Direct link to Verify the connection") After the host reports that it is connected, start a new conversation and ask something explicit, such as "Search Glean for our engineering onboarding guide." Confirm that the host shows the Glean server and tools, asks for authorization when required, and returns only content you can access in Glean. If the server is connected but tools do not load, or a verification request fails, follow [Remote MCP troubleshooting](/guides/mcp/troubleshooting.md). Hosts choose which tools to call, so name the data or action you want when you test. ## Install individually or manage centrally[​](#install-individually-or-manage-centrally "Direct link to Install individually or manage centrally") ### [Individual installation](https://docs.glean.com/user-guide/mcp/end-user-configuration) [Use the MCP Configurator to connect a user-configurable host for yourself.](https://docs.glean.com/user-guide/mcp/end-user-configuration) ### [Organization-managed setup](https://docs.glean.com/administration/platform/mcp/about) [Configure hosts that your organization manages through administrator controls.](https://docs.glean.com/administration/platform/mcp/about) ### [MDM deployment](https://docs.glean.com/administration/platform/mcp/mdm-mcp) [Deploy a Glean Remote MCP server to managed devices through MDM.](https://docs.glean.com/administration/platform/mcp/mdm-mcp) ### [Supported hosts](/guides/mcp/supported-hosts.md) [See configuration ownership, transport, and authentication for each listed host.](/guides/mcp/supported-hosts.md) ## Related administration guides[​](#related-administration-guides "Direct link to Related administration guides") * [About Glean MCP Servers](https://docs.glean.com/administration/platform/mcp/about) * [Set up Glean MCP server](https://docs.glean.com/administration/platform/mcp/enable-mcp-servers) * [Use the MCP Configurator](https://docs.glean.com/user-guide/mcp/usage) * [Glean OAuth Authorization Server](https://docs.glean.com/administration/oauth/authorization-server) * [Dynamic Client Registration](https://docs.glean.com/administration/oauth/dynamic-client-registration) * [Deploy MCP server with MDM](https://docs.glean.com/administration/platform/mcp/mdm-mcp) --- # Glean Plugin for Claude Code Official Glean plugin × # Glean plugin for Claude Code You use Claude Code. Glean brings the enterprise context. Installation 1 Set up the Glean MCP Server The plugins require a Glean MCP connection. Use the Glean configurator to connect Claude Code to your organization's Glean instance. Authentication uses Glean's OAuth server where enabled, or a Glean API token as a fallback. [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=claude-code) 2 Add the Glean marketplace and install the plugin Run these commands inside Claude Code to install the Glean enterprise-knowledge plugin. Its skills — search, code exploration, people, meetings, onboarding, productivity — auto-trigger by task. Add the Glean marketplace ``` /plugin marketplace add gleanwork/claude-plugins ``` Install the plugin ``` /plugin install glean@glean-plugins ``` --- # Glean Plugin for Codex Official Glean plugin × # Glean plugin for Codex You use Codex. Glean brings the enterprise context. Installation 1 Get your Glean MCP server details The plugin requires a Glean MCP connection. Open the Glean MCP configurator and copy your organization's full server URL — you'll use it in the last step. [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=codex) 2 Add the Glean marketplace and install the plugin Run these commands in your terminal to install the Glean enterprise-knowledge plugin. Add the Glean marketplace ``` codex plugin marketplace add gleanwork/codex-plugins ``` Install the plugin ``` codex plugin add glean@glean-codex-plugins ``` 3 Connect the Glean MCP server Before starting Codex, connect the MCP server — substitute the full server URL you copied in step 1. Then start a new Codex task so the bundled skills and MCP tools are available. Connect and sign in ``` codex mcp add glean --url codex mcp login glean ``` --- # Glean Plugin for Cursor Official Glean plugin × # Glean plugin for Cursor You use Cursor. Glean brings the enterprise context. Installation 1 Set up the Glean MCP Server The plugin requires a Glean MCP connection. Use the Glean configurator to connect Cursor to your organization's Glean instance. Authentication uses Glean's OAuth server where enabled, or a Glean API token as a fallback. [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=cursor) 2 Install the Glean Plugin Find and install the Glean plugin from the Cursor Marketplace. 1. In Cursor, open Customize → Plugins, or browse the Cursor Marketplace 2. Search for "Glean" 3. Click Add to Cursor ![Install the Glean Plugin](/img/cursor-plugin-install.png) --- # Supported MCP Hosts Glean works with a range of MCP host applications, including IDEs, CLIs, desktop apps, and web apps. Filter by configuration ownership (user-configurable vs. organization-managed) or by client type, or search by name. Each card shows who configures the connection, supported transports and authentication, and the matching setup action. Configuration AllUser-configurableOrganization-managed Type AllCLIDesktopIDEWeb SearchFilter by name… 21 of 21 ### Antigravity IDE * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=antigravity)[Vendor documentation](https://antigravity.google/docs/get-started) ### Antigravity CLI CLI * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=antigravity-cli)[Vendor documentation](https://antigravity.google/docs/cli-overview) ### ChatGPT Web * Configuration Organization-managed * Transport HTTP * Authentication API token, OAuth via DCR [Set up Glean](https://chatgpt.com/admin/apps?tab=available\&q=glean)[Vendor documentation](https://platform.openai.com/docs/mcp#test-and-connect-your-mcp-server) ### Claude Code CLI * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=claude-code)[Vendor documentation](https://code.claude.com/docs/en/mcp) ### Claude for Desktop Desktop * Configuration User-configurable * Transport STDIO * Authentication Not specified. See host documentation. [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=claude-desktop)[Vendor documentation](https://support.claude.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp) ### Claude for Teams/Enterprise Web * Configuration Organization-managed * Transport HTTP * Authentication API token, OAuth via DCR [Set up Glean](https://claude.ai/directory/connectors/glean)[Vendor documentation](https://support.claude.com/en/articles/11175166-getting-started-with-custom-connectors-using-remote-mcp) ### Codex CLI * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=codex)[Vendor documentation](https://developers.openai.com/codex/mcp) ### Cursor IDE * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=cursor)[Vendor documentation](https://cursor.com/docs/context/mcp) ### Cursor Agent CLI * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=cursor-agent)[Vendor documentation](https://cursor.com/docs/context/mcp) ### Cursor Team MCP Servers Web * Configuration Organization-managed * Transport HTTP * Authentication API token, OAuth via DCR [Set up Glean](https://cursor.com/dashboard/integrations)[Vendor documentation](https://cursor.com/docs/mcp#team-mcp-distribution) ### Gemini CLI CLI * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=gemini)[Vendor documentation](https://github.com/google-gemini/gemini-cli) ### Gemini Enterprise Web * Configuration Organization-managed * Transport HTTP * Authentication Not specified. See host documentation. [Organization setup guidance](https://docs.glean.com/administration/platform/mcp/about)[Vendor documentation](https://docs.cloud.google.com/gemini/enterprise/docs/connectors/custom-mcp-server/set-up-custom-mcp-server) ### Goose DesktopCLI * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=goose)[Vendor documentation](https://github.com/block/goose) ### JetBrains AI Assistant IDE * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=jetbrains)[Vendor documentation](https://www.jetbrains.com/help/ai-assistant/mcp.html) ### Junie (JetBrains) IDE * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=junie)[Vendor documentation](https://junie.jetbrains.com/docs/junie-cli-mcp-configuration.html) ### LibreChat Web * Configuration Organization-managed * Transport HTTP * Authentication API token, OAuth via DCR [Organization setup guidance](https://docs.glean.com/administration/platform/mcp/about)[Vendor documentation](https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/mcp_servers) ### Linear Web * Configuration Organization-managed * Transport HTTP * Authentication API token, OAuth via DCR [Organization setup guidance](https://docs.glean.com/administration/platform/mcp/about)Vendor documentation unavailable ### Microsoft Copilot Studio Web * Configuration Organization-managed * Transport HTTP * Authentication API token, OAuth via DCR [Organization setup guidance](https://docs.glean.com/administration/platform/mcp/about)[Vendor documentation](https://learn.microsoft.com/en-us/microsoft-copilot-studio/mcp-add-existing-server-to-agent) ### OpenCode CLI * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=opencode)[Vendor documentation](https://opencode.ai/docs/mcp-servers/) ### VS Code IDE * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=vscode)[Vendor documentation](https://code.visualstudio.com/docs/copilot/customization/mcp-servers) ### Windsurf IDE * Configuration User-configurable * Transport STDIO, HTTP * Authentication API token, OAuth via DCR [Open MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true\&mcpHost=windsurf)[Vendor documentation](https://docs.windsurf.com/windsurf/cascade/mcp) info This list comes from `@gleanwork/mcp-config-schema`, the published host catalog. Glean also works with **any MCP-spec-compliant client**. In the MCP Configurator, choose **Custom** and connect using your Glean MCP server URL. --- # Troubleshoot Remote MCP connections Use this guide for a tenant's Glean Remote MCP Server. If you only need Glean developer documentation in a coding assistant, use the public [Docs MCP Server](/docs-mcp.md) instead. ## The Configurator or host is missing[​](#the-configurator-or-host-is-missing "Direct link to The Configurator or host is missing") Glean enables the OAuth Authorization Server and Remote MCP Server by default for eligible tenants. Administrators can disable MCP or hide the end-user Configurator. If the [MCP Configurator](https://app.glean.com/settings/install?mcpConfigure=true) is missing, ask a Glean administrator to confirm that at least one MCP server is enabled and that the Configurator is visible. If your host is not listed in the Configurator, use the server URL with the host's own MCP setup instructions. One server URL works across MCP hosts. Administrators should start with [Set up Glean MCP server](https://docs.glean.com/administration/platform/mcp/enable-mcp-servers). After the check, reopen the Configurator and copy the server URL. Select a host only when it is listed and you want tailored setup instructions. ## DCR or redirect URI is rejected[​](#dcr-or-redirect-uri-is-rejected "Direct link to DCR or redirect URI is rejected") A tenant sets one of three Dynamic Client Registration (DCR) policies: allow any application, allow only approved applications, or do not allow dynamic registration. Registration fails when the policy restricts it to approved applications and the host's redirect URI matches no allowed pattern, or when DCR is off. For a listed host, copy its configuration from the MCP Configurator. For an unlisted host, follow that host's own MCP and OAuth setup instructions. Do not substitute a redirect URI from a different host. New and previously unconfigured default-on MCP tenants use the Glean-managed list of approved applications. Tenants with existing MCP or OAuth configuration keep their settings. Ask the administrator to check the [DCR policy](https://docs.glean.com/administration/oauth/dynamic-client-registration) after a registration or redirect URI failure. When DCR is unavailable or its scopes do not fit a governed application, use a [static OAuth client](https://docs.glean.com/administration/oauth/static-client-registration). See [OAuth authentication](/api-info/client/authentication/oauth.md) for the DCR versus static model. ## Authentication fails or sign-in loops[​](#authentication-fails-or-sign-in-loops "Direct link to Authentication fails or sign-in loops") Revoke the grant on the Glean side, then clear it on the host side. In Glean, open **Your settings**, go to **Third party apps and MCP**, select the host, and choose **Revoke**. Then remove the host's stored Glean MCP credentials, reconnect the server, and complete your SSO sign-in again. Prefer OAuth. Use a Glean-issued API token only when the host cannot complete OAuth. Do not assign API Token Creator in bulk to work around OAuth. See the [Glean OAuth Authorization Server guide](https://docs.glean.com/administration/oauth/authorization-server) and the [authentication overview](/get-started/authentication.md). ## The server URL points to the wrong server[​](#the-server-url-points-to-the-wrong-server "Direct link to The server URL points to the wrong server") A server URL identifies a Glean MCP server, not a host, so the same URL works across hosts. If the host uses a URL for a different server, return to the MCP Configurator, choose the intended server, and copy its URL again. Restart the host after you replace the configuration. For a listed host, follow its tailored setup instructions in the Configurator. For organization-managed hosts, use the host's managed-configuration guidance from [About Glean MCP Servers](https://docs.glean.com/administration/platform/mcp/about). For device-managed deployment, use the [MDM deployment guide](https://docs.glean.com/administration/platform/mcp/mdm-mcp). ## The server connects but tools are not available[​](#the-server-connects-but-tools-are-not-available "Direct link to The server connects but tools are not available") Ask an administrator to confirm that the required tools are enabled on that server. Reload or reconnect the host so it refreshes the tool list. You only receive results you can access in Glean. Hosts choose which tools to call. Test with a direct prompt such as "Search Glean for the engineering onboarding guide." Confirm the host displays the expected Glean server and tool use. See [create MCP servers](https://docs.glean.com/administration/platform/mcp/create-mcp-servers). ## Verification fails[​](#verification-fails "Direct link to Verification fails") Start a new conversation after connecting. Make an explicit Glean request and confirm it returns content you can access in Glean. If the host does not select a Glean tool, name the action or data source in the prompt. If it selects a tool but the call fails, record the error and follow the matching section above. ## Escalate with useful details[​](#escalate-with-useful-details "Direct link to Escalate with useful details") When you contact your Glean administrator or support, include: * Host name and version, operating system, and whether the connection is individual, organization-managed, or deployed through MDM. * The server name, a sanitized server URL, the time of the failure, and the exact error message. * Whether OAuth sign-in completed, DCR or redirect registration was rejected, and whether the server and tools appeared in the host. * The verification prompt and whether the host selected a Glean tool. Never include bearer tokens, API tokens, client secrets, or other credentials in a support request. For administrator-side diagnostics, see [Troubleshoot MCP Connectivity](https://docs.glean.com/administration/platform/mcp/troubleshooting). --- # Datasource Filters Besides the [general filters](/guides/search/filtering-results.md), some filters are specific to one datasource. For example, Confluence has the `author` facet, and Slack has the `channel` facet. Custom datasources pushed through the Indexing API can also define their own facets. Using the Platform API? Platform Search discovers filters with [List search filters](/api/platform-api/platform-search-filters.md) instead of `facetResults`. For a runnable example, see the [Search with discovered filters](/cookbook/search-with-discovered-filters.md) recipe. To uncover these datasource-specific facets, you can use our Glean UI to filter by the datasource you’re curious about. You will be able to find a list of facets for your search results, and facet values on the sidebar of the Glean UI (see image below). ![Datasource specific facets](/assets/images/custom-operators-ffa1a4364d9bf89b4c7385eb918e9190.png) ### Getting possible facets via Search API[​](#getting-possible-facets-via-search-api "Direct link to Getting possible facets via Search API") To get the same facets and values that populate the Glean UI sidebar, send a [search](/api/client-api/search/search.md) request like this: ``` { "query": "test", "pageSize": 10, "requestOptions": { "facetBucketSize": 3000, "facetFilters": [ { "fieldName": "app", "values": [ { "value": "confluence", "relationType": "EQUALS" } ] } ] } } ``` This will return a top-level field, [`facetResults`](/api/client-api/search/search.md#responses). facetResults has the following relevant fields: 1. [`sourceName`](/api/client-api/search/search.md#responses) - same as the facet `fieldName` in the `facetFilter`s object 2. [`operatorName`](/api/client-api/search/search.md#responses) - a display hint, `SelectSingle` or `SelectMultiple` 3. [`buckets`](/api/client-api/search/search.md#responses) - a list of facet bucket objects corresponding to a facet value 1. The facet bucket object has the following relevant fields: 1. [`count`](/api/client-api/search/search.md#responses) - the number of search results that would be returned if filtering by the facet value 2. [`value`](/api/client-api/search/search.md#responses) - the facet value (ie “engineering” for the space facet) 1. [`stringValue`](/api/client-api/search/search.md#responses) - the string value 2. [`integerValue`](/api/client-api/search/search.md#responses) - the integer value (not common) 3. [`displayLabel`](/api/client-api/search/search.md#responses) - alternative value used for display in the UI 4. [`iconConfig`](/api/client-api/search/search.md#responses) - optional image used to represent the facet value, such as a profile picture for people facet values. ``` { "sourceName": "space", "operatorName": "SelectMultiple", "buckets": [ { "count": 3, "value": { "stringValue": "engineering" } } ] } ``` The `sourceName` values in `facetResults` are the facets you can use with that datasource. Pass a bucket's `stringValue` unchanged as the filter value. If latency is a concern, and you only want to receive `facetResults`, you can send a request with [`pageSize`](/api/client-api/search/search.md#request) = 0, and add "FACET\_RESULTS" within the [`responseHints`](/api/client-api/search/search.md#request) field in [`requestOptions`](/api/client-api/search/search.md#request) to not retrieve any documents and only retrieve facetResults. See sample request body below, which would gather `facetResults` for all confluence specific facets: ``` { "query": "test", "pageSize": 0, "requestOptions": { "facetBucketSize": 3000, "facetFilters": [ { "fieldName": "app", "values": [ { "value": "confluence", "relationType": "EQUALS" } ] } ], "responseHints": ["FACET_RESULTS"] } } ``` --- # Faceted Filtering You can narrow the facet values returned in [`facetResults`](/api/client-api/search/search.md#responses) by prefix. This is useful for type-ahead filter pickers. To filter the [`buckets`](/api/client-api/search/search.md#responses) of one [`sourceName`](/api/client-api/search/search.md#responses), set the [`facetBucketFilter`](/api/client-api/search/search.md#request) object in `requestOptions`. It has two fields: 1. [`facet`](/api/client-api/search/search.md#request) - The facet fieldName that you want to filter on. 2. [`prefix`](/api/client-api/search/search.md#request) - The prefix by which you want to filter the buckets of that facet by. Include `FACET_RESULTS` in [`responseHints`](/api/client-api/search/search.md#request). This request filters the `type` buckets by the prefix `co`: ``` { "query": "test", "requestOptions": { "facetBucketFilter": { "facet": "type", "prefix": "co" }, "facetBucketSize": 3000, "responseHints": ["FACET_RESULTS"] } } ``` Within the `facetResults` field in the response to this query, you will find that the bucket for `sourceName`="type" contains only those values with the prefix "co" in any word. The required `facetBucketSize` sets the maximum number of buckets returned for each `sourceName`. The example above allows 3000. To find which facets a datasource supports, see [Datasource Filters](/guides/search/datasource-filters.md). Platform API users get query-specific filter suggestions from [List search filters](/api/platform-api/platform-search-filters.md); see the [Search with discovered filters](/cookbook/search-with-discovered-filters.md) recipe. ### Preferred Name prefix matching[​](#preferred-name-prefix-matching "Direct link to Preferred Name prefix matching") For `sourceName` supporting people within the buckets (e.g., "from", "owner", etc.), we support prefix matching using their preferred names (or nicknames). So if `facetBucketFilter` is applied to the “from” facet using the prefix “Adi,” it will give all the people whose either name or preferred name has a prefix of “Adi”. --- # Filtering Results Filters narrow the results of a Client API [search](/api/client-api/search/search.md) request. Some filters apply to every document; others are specific to a datasource. This guide covers the general filters. To find the filters a datasource supports, see [Datasource Filters](/guides/search/datasource-filters.md). Using the Platform API? Platform Search uses a different filter model: `filters`, `time_range`, and filter discovery through [List search filters](/api/platform-api/platform-search-filters.md). For a runnable example, see the [Search with discovered filters](/cookbook/search-with-discovered-filters.md) recipe. ## How to Use[​](#how-to-use "Direct link to How to Use") To filter results by field, pass a list of `facetFilter` objects in [`requestOptions.facetFilters`](/api/client-api/search/search.md#request). `requestOptions` also requires `facetBucketSize`. Each facetFilter object has the following relevant fields: | Field Name | Description | | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `fieldName` | the name of the field we are filtering by (eg “from” to facet by user, “type” for document type, etc). fieldName should be unique in the list of facetFilter objects. | | `values` | a list of facetFilterValue objects. All values are OR’d between the same field name (we AND between different field names). | A facetFilterValue object has the following relevant fields: | Field Name | Description | | -------------- | --------------------------------------------------------------------------------------------------------------- | | `value` | string value that results are being filtered to. | | `relationType` | One of `EQUALS`, `ID_EQUALS`, `NOT_EQUALS`, `LT`, or `GT`. See [Time filters](#time-filters) for `LT` and `GT`. | ### Basic Example[​](#basic-example "Direct link to Basic Example") To return only PDF documents, set `requestOptions.facetFilters` to the following. This is equivalent to adding `type:pdf` to the query. ``` [ { "fieldName": "type", "values": [ { "relationType": "EQUALS", "value": "pdf" } ] } ] ``` ## Universal Field Names[​](#universal-field-names "Direct link to Universal Field Names") ### Topbar Facet Field Names[​](#topbar-facet-field-names "Direct link to Topbar Facet Field Names") | Field Name | Description | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `last_updated_at` | Filter by document last updated at | | `from` | Filter by user who created/modified the document. Supports special value "me" for the current user | | `suggested` | Filter by suggestion: `my history` for documents in the current user's history (`my:history`), or the go links value (`has:golink`). Copy go links and other values from `facetResults` | | `collection` | Filter by collection name | | `type` | Filter by document type | ### Entity Field Names[​](#entity-field-names "Direct link to Entity Field Names") | Field Name | Description | | -------------- | --------------------------- | | `businessunit` | Filter by business unit | | `city` | Filter by city | | `country` | Filter by country | | `industry` | Filter by industry | | `location` | Filter by location | | `region` | Filter by region | | `roletype` | Filter by role type | | `startafter` | Filter by start date after | | `startbefore` | Filter by start date before | | `state` | Filter by state | | `title` | Filter by title | | `reportsto` | Filter by reporting to | ## Exceptions to the basic example[​](#exceptions-to-the-basic-example "Direct link to Exceptions to the basic example") ### Time filters[​](#time-filters "Direct link to Time filters") Time filters are the only exception to the rule. The `fieldName` is always “last\_updated\_at”, and we use different relationTypes to specify different time ranges. We support 2 types of values: specific dates and special values. #### Specific dates[​](#specific-dates "Direct link to Specific dates") Use the “GT” and “LT” `relationTypes` to specify a date range. The ranges can also be open-ended (only include a GT or an LT). Each date value should be in the form `YYYY-MM-DD` passed in as a string. Note that when using GT and LT, the values are noninclusive (eg using {`relationType`=”GT”, value=”2023-06-17”} will include dates from 2023-06-18 and later). All dates provided will begin with the “start of the day” (12:00 am). Dates will end at the end of the day (11:59:59 pm). Closed date range example for filtering to documents from dates 6/16, 6/17, 6/18, 6/19: ``` [ { "fieldName": "last_updated_at", "values": [ { "relationType": "GT", "value": "2023-06-15" }, { "relationType": "LT", "value": "2023-06-20" } ] } ] ``` Open date range example for filtering to documents from dates 6/11 onwards: ``` [ { "fieldName": "last_updated_at", "values": [ { "relationType": "GT", "value": "2023-06-10" } ] } ] ``` #### Special Values[​](#special-values "Direct link to Special Values") For special values, we allow the values `past_day, past_week, past_month, yesterday, today, past_n_days, past_n_weeks, past_n_months, past_n_years `for the relation type `EQUALS`, where n is a number, ie 5 in `past_5_days`. For all past\* prefixed values, we also support the last\* prefix, they mean the same thing (ie `last_week` is a viable substitute for `past_week`). We allow the values `past_day`, `past_week`, `past_month`, `yesterday`, and `today` for the relation type `LT`. We allow the value `yesterday` for the relation type `GT`. Use only the values listed above. If you are used to using operators and values in the query string, here are some examples of translations of query string value to REST API value. Sample: `updated:today` becomes ``` [ { "fieldName": "last_updated_at", "values": [{ "relationType": "EQUALS", "value": "today" }] } ] ``` `before:past_week` becomes ``` [ { "fieldName": "last_updated_at", "values": [{ "relationType": "LT", "value": "past_week" }] } ] ``` `after:yesterday` becomes ``` [ { "fieldName": "last_updated_at", "values": [{ "relationType": "GT", "value": "yesterday" }] } ] ``` #### Timezone considerations[​](#timezone-considerations "Direct link to Timezone considerations") Time filters use the user's timezone, except for `past_day`, `past_week`, `past_month`, and `past_year` with the relation type `EQUALS`. Those values do not account for timezone. ### History filter[​](#history-filter "Direct link to History filter") The `my:history` query operator shows only documents the user has viewed. In `facetFilters`, it is the `suggested` field with the value `my history`: ``` { "fieldName": "suggested", "values": [{ "relationType": "EQUALS", "value": "my history" }] } ``` ### From filter (or any user filter):[​](#from-filter-or-any-user-filter "Direct link to From filter (or any user filter):") To choose one person when several share a name, use the email address they sign in to Glean with as the `value`. For example, `user@example.com` and `userone@example.com` filter to different people named "User One". The query `from:"User One" updated:today type:document` becomes: ``` [ { "fieldName": "from", "values": [{ "relationType": "EQUALS", "value": "userone@example.com" }] }, { "fieldName": "last_updated_at", "values": [{ "relationType": "EQUALS", "value": "today" }] }, { "fieldName": "type", "values": [{ "relationType": "EQUALS", "value": "document" }] } ] ``` --- # Returning LLM Content Over Snippets You can receive expanded content by setting 2 fields in the request body: [`maxSnippetSize`](/api/client-api/search/search.md#request) and [`returnLlmContentOverSnippets`](/api/client-api/search/search.md#request). ## Request Parameters[​](#request-parameters "Direct link to Request Parameters") 1. [`maxSnippetSize`](/api/client-api/search/search.md#request) - Gives a hint to the server on how many characters of expanded content should be returned per result. The server may return less or more. This field must be set if [`returnLlmContentOverSnippets`](/api/client-api/search/search.md#request) is true, and it must be an integer value greater than 0 and less than or equal to 10000. 2. [`returnLlmContentOverSnippets`](/api/client-api/search/search.md#request) - If true, the server will return expanded content. If not, the server will return SERP results as expected. The current retrieval process and formatting of content are what we use internally as LLM content, which we are still modifying for internal usage. ## Sample Request[​](#sample-request "Direct link to Sample Request") Sample request body (contains all required fields at minimum to get expanded content): ``` { "query": "mentions", "pageSize": 10, "maxSnippetSize": 4000, "requestOptions": { "returnLlmContentOverSnippets": true } } ``` ## Reading the Search Response[​](#reading-the-search-response "Direct link to Reading the Search Response") The LLM content is formatted differently than the non-LLM content you get from a regular [/search REST API](/api/client-api/search/overview.md) request. In the response, there is a top-level field `results`. `results` is an array of objects, each representing a search result. Each search result has a top-level field `snippets` which is an array of text content from the search result. ### Non-LLM Content[​](#non-llm-content "Direct link to Non-LLM Content") The format of the content is different from LLM content in that the snippets are ordered by score, not in the order they appear in the document, and we include formatting instructions to bold terms in the content that match the query. * **mimeType**: The type of text * **ranges**: The indexes of text that we should apply special formatting to, to indicate a term matching in the query * The `endIndex` is where the formatting should end * The `startIndex` is when the formatting should begin * The `type` is the kind of formatting we should do on this range * **snippet**: Deprecated * **fullTextList**: The content for slack/other messaging documents only * **text**: The content * **snippetTextOrdering**: The order of where the snippet appears in the original document ``` { "results": [ { ... more fields... "snippets": [ { "mimeType": "text/plain", "ranges": [ { "endIndex": 16, "startIndex": 8, "type": "BOLD" } ], "snippet": "", "snippetTextOrdering": 1, "text": "Testing mentions" }, { "mimeType": "text/plain", "ranges": [ { "endIndex": 13, "startIndex": 5, "type": "BOLD" } ], "snippet": "", "snippetTextOrdering": 2, "text": "This mentions user 1" } ... more snippets ... ] ... more fields ... } ] } ``` ### LLM Content[​](#llm-content "Direct link to LLM Content") We do not bold hits for LLM content so we do not include the ranges field. * **mimeType** stays the same * For slack documents, we still only populate `fullTextList` for content * We order snippets in order of the document, not by score, so we do not include `snippetTextOrdering` ``` { "results": [ { ... more fields... "snippets": [ { "mimeType": "text/plain", "text": "Testing mentions", "snippet": "" }, { "mimeType": "text/plain", "text": "This mentions user 1", "snippet": "" } ] ... more snippets ... } ... more fields ... ] } ``` ## FAQ[​](#faq "Direct link to FAQ") ### Will maxSnippetSize be honored when returnLlmContentOverSnippets is False?[​](#will-maxsnippetsize-be-honored-when-returnllmcontentoversnippets-is-false "Direct link to Will maxSnippetSize be honored when returnLlmContentOverSnippets is False?") We will try to hit the `maxSnippetSize` passed in, it is not clamped at 255 characters. However this is still a "hint" number and we may not meet it exactly due to wanting to respect word boundaries, and also due to the number of matches to the query there are in the text content. ### Is text generated when using returnLlmContentOverSnippets?[​](#is-text-generated-when-using-returnllmcontentoversnippets "Direct link to Is text generated when using returnLlmContentOverSnippets?") No text is generated, all text returned should be also present in the original doc. We might not return any snippet at all if there is not enough content to return. ### Is there a cap on the maximum number of snippets returned per document?[​](#is-there-a-cap-on-the-maximum-number-of-snippets-returned-per-document "Direct link to Is there a cap on the maximum number of snippets returned per document?") Yes, there is a cap at 10,000 characters. We have found that for our LLM purposes that use snippets, we've never needed more than 5000 characters for snippets, so we introduced a cap on both character count and doc count as to prevent sending too large of a response back. ### Why do I see irrelevant content when using returnLlmContentOverSnippets?[​](#why-do-i-see-irrelevant-content-when-using-returnllmcontentoversnippets "Direct link to Why do I see irrelevant content when using returnLlmContentOverSnippets?") When we return snippets (`returnLlmContentOverSnippets = false`), snippets are limited to around 255 characters, so almost always, all the content returned is content related to or matching the search query. When you ask for LLM Content (`returnLlmContentOverSnippets = true`), you can request up to 10,000 characters using `maxSnippetSize`, and we will try to return as many characters as requested. So if the document does not have any more matches/relevant content to the search query, we will add extra content that surrounds the matches/relevant content to hit `maxSnippetSize`. For example, if the query is "test" and the document contains: ``` ...more text... My favorite fruit is apples. I don't like taking tests. Today is Monday. ... more text ... ``` We would get a snippet of "I don't like taking tests". But if `returnLlmContentOverSnippets = true` and we have a larger `maxSnippetSize` than the length of "I don't like taking tests", then we would try to expand the content returned to the text above and below "I don't like taking tests" and include the text in "My favorite…" and "Today is.." in the content returned. The surrounding text we include may or may not be relevant content, and that is why you may see content that is not relevant to the search query. ## Tips on Usage[​](#tips-on-usage "Direct link to Tips on Usage") * If you only want relevant content, then you can use snippets directly (set `returnLlmContentOverSnippets = false`) * You can also increase/decrease the `maxSnippetSize`, an increase would introduce more surrounding content that is potentially irrelevant * For our LLM usage, we use 4000 for `maxSnippetSize`, which includes a good amount of surrounding content. While the surrounding content would not be a semantic match, it can sometimes provide useful context that gives us better quality answers ## Still in Development[​](#still-in-development "Direct link to Still in Development") We are constantly making improvements to our content retrieval, and have several projects in flight to improve the quality of content retrieved. You can expect that this API will stay up to date with what we currently use in our LLM product! ## Next Steps[​](#next-steps "Direct link to Next Steps") ### [Search API](/api/client-api/search/overview.md) [Explore the complete Search API documentation](/api/client-api/search/overview.md) ### [Filtering Results](/guides/search/filtering-results.md) [Learn how to filter and refine search results](/guides/search/filtering-results.md) --- # Authentication ## Overview[​](#overview "Direct link to Overview") Glean's authentication system supports the OAuth 2.0 standard for securing tool requests. This guide covers the available authentication types and how to verify requests from Glean. ## Authentication Types[​](#authentication-types "Direct link to Authentication Types") ![Authentication section in tools configuration](/assets/images/actions_auth_section-eb5f1695bc3db4b87b48cc87dcca7190.png) ### OAuth Admin Requires one-time authorization by the tool developer or app admin during setup. After authorization, Glean sends the token for all requests from authorized users. ### OAuth User Requires initial OAuth connection setup by the developer/admin. Users must authorize the tool on first use, after which their token is used for subsequent requests. ### None No tokens are provided in the request. Choose this if you don't require specific tokens from Glean to handle requests. warning When using "None" authentication type, it's **highly recommended** to implement request verification to ensure requests are coming from Glean. Without additional protections (e.g., VPN, Firewall), your endpoint would be publicly accessible. ## OAuth Configuration[​](#oauth-configuration "Direct link to OAuth Configuration") When setting up OAuth (admin or user), you'll need to configure the following parameters using the [OAuth authorization\_code grant type](https://oauth.net/2/grant-types/authorization-code/): | Parameter | Type | Required | Description | | --------------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Client ID** | string | ✓ | The client ID for your OAuth application | | **Client Secret** | string | ✓ | The client secret for your OAuth application | | **Client URL** | string | ✓ | The URL used to redirect users for authentication | | **Authorization URL** | string | ✓ | The URL Glean uses to complete OAuth by issuing a POST request with the authorization code | | **Scopes** | string | | Optional parameter for relevant application scopes. Consider adding `offline_access` scope if supported to ensure refresh tokens are sent ([reference](https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess)) | warning Your OAuth App must be configured to allow redirects (Callback URL) to: ``` https:///tools/oauth/verify_code ``` Find your server URL at [app.glean.com/admin/about-glean](https://app.glean.com/admin/about-glean). Without this, the OAuth integration may fail or show invalid redirect URI errors. ## Request Verification[​](#request-verification "Direct link to Request Verification") info While optional, implementing request verification is highly recommended to enhance the security of your tool server endpoints. Glean provides a JWT-based signature in the `Glean-Tools-Signature` header, signed using RSA-SHA256. The signature can be verified using your Glean instance's public key. ### JWT Claims[​](#jwt-claims "Direct link to JWT Claims") The JWT header includes these standard claims: 1. `iat` - Issued at time 2. `exp` - Expiration time 3. `iss` - Issuer (always set to 'glean') ### Implementation Examples[​](#implementation-examples "Direct link to Implementation Examples") * Python * Java ``` import jwt import json import requests # Fill your glean instance here. YOUR_GLEAN_SERVER_URL='' # Find your server URL at app.glean.com/admin/about-glean # Use this function as is in your code (once you have filled out YOUR_GLEAN_SERVER_URL). # Pass the header value for Glean-Tools-Signature as the 'token' in this function. def verify_jwt(token): try: # First, we fetch the public key JSON response. response = requests.get(f"{YOUR_GLEAN_SERVER_URL}/api/tools/v1/verification_key") response.raise_for_status() # Raises an exception for 4XX/5XX responses public_key_str = response.json()['publicKey'] # Second, we convert this into the PEM format. pem_key = f"-----BEGIN PUBLIC KEY-----\n{public_key_str}\n-----END PUBLIC KEY-----" # Finally, we attempt to decode the token using the public key. decoded = jwt.decode(token, pem_key, algorithms=['RS256'], issuer='glean') return True except jwt.PyJWTError as e: # Handle error (e.g., token expired, token tampered, etc.) print(f"JWT verification failed: {e}") return False ``` ``` import java.util.Base64; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import org.jose4j.jwa.AlgorithmConstraints; import org.jose4j.jwt.consumer.InvalidJwtException; import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; public static void verifySignature(String publicKey, String jwtFromHeader) throws IOException { byte[] derKey = Base64.getDecoder().decode(publicKey); final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey); PublicKey rsaOrEcKey; try { rsaOrEcKey = KeyFactory.getInstance(JWT_ALG).generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { // This should never happen, since we're using a standard algorithm. throw new RuntimeException(e.getMessage()); } catch (InvalidKeySpecException e) { throw new RuntimeException( "Unhandled exception during public key setup: " + e.getMessage()); } AlgorithmConstraints constraints = new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST, "RS256"); JwtConsumer jwtConsumer = new JwtConsumerBuilder() .setRequireExpirationTime() .setRequireIssuedAt() .setAllowedClockSkewInSeconds(30) .setExpectedIssuer("glean") .setVerificationKey(rsaOrEcKey) .setJwsAlgorithmConstraints(constraints) .build(); try { jwtConsumer.processToClaims(jwtFromHeader); } catch (InvalidJwtException e) { throw new IOException("Failed to verify tools signature: " + e.getMessage()); } } ``` --- # Creating Tools ## Overview[​](#overview "Direct link to Overview") Tools allow you to create automated workflows and integrations within Glean. This guide walks through the process of creating and configuring a Tool. note You must have an `Admin` or `Tool creator` role to create tools. To get started, navigate to [Admin console > Platform > Tools](https://app.glean.com/admin/platform/tools) where you'll see a list of all available tools. Click the "Add" button to begin. You can choose to create a custom tool from scratch, build a simple redirect tool or pick from one of the available out-of-the-box tools (if enabled for you). tip For common applications like Jira and Salesforce, Glean offers first-party, out-of-the-box tools that require no coding — select one of the available out-of-the-box tools (if enabled for you) instead of building from scratch. For other use cases, create a custom tool from scratch. If you select creating a tool from scratch, these are the steps to create a tool: 1 ### Basic Information Start by providing the essential details to identify your tool: | Field | Type | Required | Description | | ----------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | **Display name** | string | ✓ | The name that will be shown on the Tools page | | **Display description** | string | ✓ | A clear description of what your tool does | | **Unique identifier** | string | ✓ | A unique identifier to distinguish your tool from others with similar names | | **Tool type** | string | ✓ | Choose between: **Write** (performs operations in external apps) or **Read (retrieval)** (fetches information from external applications) | #### Tool Types in Detail[​](#tool-types-in-detail "Direct link to Tool Types in Detail") ### Write Helps users perform operations in external apps. Can be either: * **Execution**: Runs inside Glean — Glean calls the external API directly * **Redirect**: Sends users to the appropriate external URL ### Read (retrieval) Fetches information from external applications that may or may not be indexed with Glean 2 ### Trigger Condition Configure when your tool should be triggered in Glean Chat. [![Trigger condition configuration](/assets/images/actions-trigger-condition-637c297eeb0988cf8530b411ab980af9.png)](/assets/files/actions-trigger-condition-637c297eeb0988cf8530b411ab980af9.png) info When users interact with Glean Chat, the system matches their requests against the trigger conditions to determine which tool to use. For example, for an IT support Tool, you might include trigger conditions like: "Creates IT support tickets on JIRA. Use this tool when the user wants to create a support ticket, needs access to something or wants help with any IT related issues." You can also provide example queries such as: * "I forgot my password for Notion" * "Need to reset my password for gmail" * "I need access to gong" 3 ### Functionality Define the specific configurations for your tool. For tools created from scratch, you'll need to upload an API spec: [![API spec upload](/assets/images/actions-functionality-spec-8e4ee43b22d41da7881f8e1689da4dcd.png)](/assets/files/actions-functionality-spec-8e4ee43b22d41da7881f8e1689da4dcd.png) 4 ### Authentication note This step is only required for tools with a server. Configure how Glean should pass authentication information for requests coming from Glean to your tool's server. ## Testing and Deployment[​](#testing-and-deployment "Direct link to Testing and Deployment") ### Testing Your Tool[​](#testing-your-tool "Direct link to Testing Your Tool") After saving your tool, use the testing link provided at the bottom of the page to verify its functionality. Test with various queries that should trigger the tool and refine the Trigger Condition if needed. ### Deployment Options[​](#deployment-options "Direct link to Deployment Options") Tools can be enabled on the following surfaces, each controlled by an independent toggle: ### Chat Let users invoke the tool conversationally in Glean Assistant. This toggle only appears for tools validated for conversational, single-turn use. ### Agents Make the tool available as a step when building or running Glean Agents. ### Glean MCP Server Make the tool available through the Glean MCP server for all or some teammates. ## API Specification Configuration[​](#api-specification-configuration "Direct link to API Specification Configuration") note This section only applies to custom tools built from scratch. When configuring the API spec for your tool, provide a YAML or JSON file that follows these requirements: * Contains a single endpoint defined in /paths (e.g., /execute) * Fields should be added in either requestBody.content or parameters * All fields must follow this format: * No nested fields (only string/number/integer/boolean or arrays of these types) * Use enum with a single value for fixed values * Assistant can guess non-fixed values * Mark required fields using standard OpenAPI specifications For an example custom tool built from an OpenAPI spec, see [Custom tools](https://docs.glean.com/tools/custom) in the Glean documentation. --- # Google Calendar Events Retrieval Tool ## Overview[​](#overview "Direct link to Overview") This guide walks through creating a retrieval tool that allows Glean Assistant to access and display Google Calendar events. This integration enables users to query their calendar events directly through Glean's interface with proper OAuth authentication and secure API access. This is a **retrieval tool** that fetches calendar data from Google Calendar API with OAuth User authentication. ## Prerequisites[​](#prerequisites "Direct link to Prerequisites") Before beginning this implementation, ensure you have: * Administrator access to your Glean instance * Access to Google Cloud Console with appropriate permissions * Understanding of OAuth 2.0 authentication flows ## Implementation Guide[​](#implementation-guide "Direct link to Implementation Guide") 1 Creating the Base Tool Begin by navigating to [Admin console > Platform > Tools](https://app.glean.com/admin/platform/tools) and clicking "Add". info Choose "start from scratch" since the Google Calendar tool isn't available in the templates. #### Basic Information Configuration[​](#basic-information-configuration "Direct link to Basic Information Configuration") [![Basic Info Dialog Box](/assets/images/calendar_action_basic_info-406f66eab1800124bf7d46e902aab9b3.png)](/assets/files/calendar_action_basic_info-406f66eab1800124bf7d46e902aab9b3.png) Configure these essential details: * **Display Name**: Choose a clear, descriptive name (e.g., "Calendar Events Retrieval") * **Description**: Explain the tool's purpose and capabilities * **Unique Identifier**: Set a unique ID (e.g., "retrieveCalendarEvents") * **Tool type**: Select "Read (retrieval)" since this tool reads calendar data #### Trigger Condition Setup[​](#trigger-condition-setup "Direct link to Trigger Condition Setup") [![Trigger Condition Dialog Box](/assets/images/calendar_action_trigger_condition-fcd9a0c2c946dac1163dee11d6154405.png)](/assets/files/calendar_action_trigger_condition-fcd9a0c2c946dac1163dee11d6154405.png) Your trigger condition should clearly specify: * When Glean Assistant should use this tool * Expected user query patterns * Scenarios where the tool shouldn't be used 2 Configuring the API Specification Add this OpenAPI specification to define how Glean Assistant interacts with the Google Calendar API: Click to expand the complete OpenAPI specification ``` openapi: 3.0.1 servers: - url: 'https://www.googleapis.com/calendar/v3/' info: title: Google Calendar Events API description: This API returns events on the primary calendar of the currently logged-in user. version: 1.0.0 paths: /calendars/primary/events: get: summary: List Events description: Retrieves events from the primary calendar. parameters: - name: timeMin in: query description: | Lower bound (exclusive) for an event's start time to filter by. Must be an RFC3339 timestamp with a mandatory time zone offset. Its very important to have timestamp in Z format Example: 2011-06-03T10:00:00Z required: false schema: type: string format: date-time - name: timeMax in: query description: | Upper bound (exclusive) for an event's start time to filter by. Must be an RFC3339 timestamp with a mandatory time zone offset. Its very important to have timestamp in Z format Example: 2011-06-03T10:00:00Z required: false schema: type: string format: date-time - name: q in: query description: Free text search terms to find events that match these terms in various fields such as summary, description, location, etc. required: false schema: type: string responses: '200': description: A list of events. content: application/json: schema: type: object properties: items: type: array items: type: object properties: id: type: string description: Identifier of the event. status: type: string description: Status of the event (confirmed, tentative, cancelled). summary: type: string description: Title of the event. description: type: string description: Description of the event. start: type: object properties: dateTime: type: string format: date-time description: The start time of the event. end: type: object properties: dateTime: type: string format: date-time description: The end time of the event. ``` [![Functionality Dialog Box](/assets/images/calendar_action_functionality-ca088a03866b23207c4699aec2bdadf6.png)](/assets/files/calendar_action_functionality-ca088a03866b23207c4699aec2bdadf6.png) warning Leave the "Use existing on-premise server" option unchecked since we're accessing Google's API directly. 3 Setting Up OAuth Authentication This tool requires OAuth User authentication to access individual users' calendar data. #### Google Cloud Console Setup[​](#google-cloud-console-setup "Direct link to Google Cloud Console Setup") 1. Access the [Google Cloud Console Credentials page](https://console.cloud.google.com/apis/credentials) 2. Create OAuth credentials: [![GCP Credentials Dialog Box](/assets/images/calendar_action_gcs_cloud_create_cred-4d8e446d88ee9f93318111ff4f1f3be0.png)](/assets/files/calendar_action_gcs_cloud_create_cred-4d8e446d88ee9f93318111ff4f1f3be0.png) 3. Configure as a web application: [![Create OAuth Client](/assets/images/calendar_action_create_oauth_client-834713df063c01a38edeffe9f417035a.png)](/assets/files/calendar_action_create_oauth_client-834713df063c01a38edeffe9f417035a.png) #### Redirect URI Configuration[​](#redirect-uri-configuration "Direct link to Redirect URI Configuration") Add this URI to your OAuth client (Note your instance name is typically the email domain without the TLD): ``` https://{instance-name}-be.glean.com/tools/oauth/verify_code ``` This URL can be copied from the `Callback URL` field in your tool's `Authentication` section. #### OAuth Settings in Glean[​](#oauth-settings-in-glean "Direct link to OAuth Settings in Glean") Configure these parameters in Glean's `Authentication` section: ``` Client URL: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent Authorization URL: https://oauth2.googleapis.com/token Scopes: https://www.googleapis.com/auth/calendar.readonly ``` note In Glean's OAuth configuration, **Client URL** is Google's user-facing authorization endpoint (where users are redirected to grant access), and **Authorization URL** is Google's token endpoint (which Glean calls to exchange the authorization code for a token). There is no separate token URL field. [![Authentication Dialogue Box](/assets/images/calendar_action_authentication-02cee3179b5ae80592f59549392402ae.png)](/assets/files/calendar_action_authentication-02cee3179b5ae80592f59549392402ae.png) #### Enable Google Calendar API[​](#enable-google-calendar-api "Direct link to Enable Google Calendar API") 1. Access your Google Cloud Console 2. Enable the Calendar API: [![GCP Google Calendar API Settings](/assets/images/calendar_action_google_api-8768d93d224f5d0a3e7b16313e744e4c.png)](/assets/files/calendar_action_google_api-8768d93d224f5d0a3e7b16313e744e4c.png) 4 Testing and Deployment Test the tool thoroughly before deployment. #### Initial Testing[​](#initial-testing "Direct link to Initial Testing") 1. Use the provided testing URL 2. You'll see the connection banner: [![Connect Tool Banner](/assets/images/calendar_action_connect_banner-5dbdfa7f4e061f54337d23e6a2c401da.png)](/assets/files/calendar_action_connect_banner-5dbdfa7f4e061f54337d23e6a2c401da.png) 3. Authenticate with Google 4. Verify event retrieval: [![Calendar Events Result](/assets/images/calendar_action_calendar_events-347471327de3f542dbba74f4409cce1b.png)](/assets/files/calendar_action_calendar_events-347471327de3f542dbba74f4409cce1b.png) #### Deployment[​](#deployment "Direct link to Deployment") Configure deployment settings: [![Deploy Tool View](/assets/images/deploy_window-b8d8202554821dd4609e315bcc92dd54.png)](/assets/files/deploy_window-b8d8202554821dd4609e315bcc92dd54.png) Set appropriate access controls: * Enable for all teammates or * Restrict to specific team members ## Best Practices[​](#best-practices "Direct link to Best Practices") ### API Specification Design[​](#api-specification-design "Direct link to API Specification Design") ### Parameter Configuration When configuring the Calendar API parameters: * Use RFC3339 format for all date-time fields * Include timezone offsets in timestamp examples * Provide clear descriptions for search parameters * Consider default values for time ranges ### Response Handling Important considerations for handling calendar data: * Process multiple calendar event types (recurring, all-day, etc.) * Handle timezone conversions appropriately * Consider pagination for large result sets * Process cancelled or declined events properly ### Authentication Implementation[​](#authentication-implementation "Direct link to Authentication Implementation") ### OAuth Setup Critical OAuth implementation details: * Always use `access_type=offline` for refresh token support * Request minimal scopes (readonly for calendar access) * Handle token refresh scenarios gracefully * Implement proper token storage and security ### Performance Optimization[​](#performance-optimization "Direct link to Performance Optimization") ### Query Optimization Optimize calendar queries by: * Using appropriate time ranges * Implementing result caching * Limiting returned fields * Managing API quota usage ### Error Handling Implement robust error handling: * Handle quota limit scenarios * Manage token expiration gracefully * Provide meaningful error messages * Implement retry logic for transient failures ### Common Implementation Challenges[​](#common-implementation-challenges "Direct link to Common Implementation Challenges") warning Watch out for these common issues: 1. **Time Zone Handling** * Inconsistent timezone formatting * Missing timezone offsets * Incorrect local time conversions 2. **Calendar Access** * Primary vs. secondary calendar confusion * Shared calendar permissions * Resource calendar access 3. **Event Filtering** * Inefficient time range queries * Missing recurring event instances * Incomplete search term matching 4. **OAuth Configuration** * Incorrect redirect URIs * Missing required scopes * Token refresh failures ## Security Considerations[​](#security-considerations "Direct link to Security Considerations") ### Security Best Practices Important security measures for calendar integration: * **Scope Minimization**: Request only necessary permissions (readonly for retrieval) * **Token Security**: Implement secure token storage and transmission * **Access Logging**: Log access patterns for security monitoring * **Data Privacy**: Ensure calendar data is handled according to privacy policies ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") If you encounter issues: 1. **Verify OAuth Configuration** * Check credential settings in Google Cloud Console * Confirm correct scopes are requested * Validate redirect URIs match exactly 2. **Check API Access** * Confirm Calendar API is enabled in Google Cloud Console * Verify quota availability and limits * Check user permissions for calendar access 3. **Debug Authentication Flow** * Test the OAuth flow manually * Check for proper token generation and refresh * Validate scope permissions 4. **Review Implementation** * Validate timestamp formats in API calls * Check parameter formatting and encoding * Verify error handling logic ## Next Steps[​](#next-steps "Direct link to Next Steps") After successful implementation: 1. **Documentation**: Create user guides for calendar integration features 2. **Monitoring**: Set up dashboards for tracking usage and performance 3. **Support**: Establish procedures for handling user issues 4. **Enhancement**: Plan feature expansions based on user feedback 5. **Reviews**: Schedule regular system and security reviews --- # Tools FAQ ## Common Questions[​](#common-questions "Direct link to Common Questions") Which fields are interpreted by LLM? The "Unique Identifier" and "Trigger Condition (including Example Queries)" fields are used by the LLM to identify which tool it should invoke/use for a user's query. Make sure to give a detailed trigger condition and example queries, to ensure LLM knows exactly what user queries this tool should be used for. How to authenticate Tools built using Glean's Jira template? Follow the steps in [Glean Documentation](https://docs.glean.com/administration/tools/setup-tools/jira-tools-setup#configure-authentication) to set up authentication for Jira. What are type hints? Type hints are like instructions that help the Glean AI understand and work with different tools better. They're clues that tell the AI what each part of a tool does, making it easier for the AI to get the right information or do the right thing with those tools. ### Benefits of Implementing Type Hints[​](#benefits-of-implementing-type-hints "Direct link to Benefits of Implementing Type Hints") Type hints provide two key advantages: * **Simplified Tool Creation**: Type hints make it easier for developers to describe their tools' capabilities and requirements. * **Better at Getting Information and Doing Tasks**: Type hints give clear instructions on gathering data or performing actions, leading to more accurate and relevant results from the AI. ### How to Use Type Hints[​](#how-to-use-type-hints "Direct link to How to Use Type Hints") To use type-hints, consider the following steps: 1. Identify Relevant Type Hints: Review the list of supported type hints to determine which ones align with your tool's functionality. 2. Incorporate Type Hints into Your API Specs: Add the selected type hints as structured details in your tool's API specifications. ### Supported Type Hints[​](#supported-type-hints "Direct link to Supported Type Hints") Here are the type hints currently supported by Glean: #### Content[​](#content "Direct link to Content") Applied to fields that deal with large amounts of text or content. Ideal for tools that generate, modify, or display content. ``` description: x-glean-typehint: 'Content' type: string description: Rich description. ``` #### EmailAddress[​](#emailaddress "Direct link to EmailAddress") Used for fields that capture the email address of the user. ``` assignee: x-glean-typehint: 'EmailAddress' type: string description: User to which the issue is assigned. ``` #### JiraComponentId[​](#jiracomponentid "Direct link to JiraComponentId") Designated for fields involving a JIRA component ID. ``` components: x-glean-typehint: 'JiraComponentId' type: string description: Component Id where the ticket should be filed. ``` #### JiraIssueId[​](#jiraissueid "Direct link to JiraIssueId") Used for fields that interact with a specific JIRA issue. ``` issue: x-glean-typehint: 'JiraIssueId' type: string description: Issue Id on which comment should be posted. ``` #### JiraIssuePriority[​](#jiraissuepriority "Direct link to JiraIssuePriority") For fields that denote the priority of a Jira issue. ``` priority: x-glean-typehint: 'JiraIssuePriority' type: integer description: Numeric priority. 1 (Highest) to 5 (Lowest) minimum: 1 maximum: 5 ``` #### JiraIssueType[​](#jiraissuetype "Direct link to JiraIssueType") For fields that denote a Jira issue type. ``` issue_type: x-glean-typehint: 'JiraIssueType' type: integer description: Issue type. 10000 refers to Epic, 10001 to Story, 10002 to Task, 10003 to Sub-Task, 10004 to Bug. ``` #### JiraProjectID[​](#jiraprojectid "Direct link to JiraProjectID") For fields requiring a JIRA project ID. ``` pid: x-glean-typehint: 'JiraProjectID' type: string description: Project ID where the ticket is created. ``` #### ResultUrl[​](#resulturl "Direct link to ResultUrl") For fields containing the URL of created objects from write tools. ``` jiraTicketUrl: x-glean-typehint: 'ResultUrl' type: string description: Url of the created object. ``` Do Tools work for endpoints behind VPN? Yes, please contact our team for information on how to do this. How much data can my service return (for retrieval tools)? The response from the service you're connecting to retrieve content using your retrieval tool will be sent to the LLM to create the final response. Hence, please ensure that the data returned is not larger than \~8000 characters. Depending on the LLM model being used, larger responses will be truncated from the end. --- # Triggers A trigger subscribes an application to content events in Glean — a review requested from you, a new call you took part in, the half hour before a meeting starts — and delivers each one to your endpoint as a signed webhook. Triggers are created from **presets**: curated event definitions, one per datasource, each declaring the inputs it accepts. You pick a preset and supply its inputs rather than assembling a query, and the catalog your deployment serves is discoverable at runtime. Learn by building[Triggers](/cookbook.md#triggers) [ collection →](/cookbook.md#triggers) 1. [Trigger a customer meeting prep request](/cookbook/customer-meeting-prep-trigger.md)Beginner · \~20 min ## How a trigger works[​](#how-a-trigger-works "Direct link to How a trigger works") 1. **Pick a preset.** `GET /api/trigger-presets` returns paginated preset summaries in `results`; filter with the `datasource` query parameter, or read the `datasource` field on each result. Preset ids are per-deployment — discover them rather than hardcoding. 2. **Get its inputs.** `GET /api/trigger-presets/{preset_id}` returns the full preset, including the `inputs` array that declares the inputs it accepts and which are required. A preset that takes none returns an empty array. 3. **Preview first.** `POST /api/trigger-presets/{preset_id}/events/search` reports the recent events the preset would have matched with those inputs. Creating a trigger validates the preset and its required inputs — it cannot tell you whether anything will ever match, so a preset that matches nothing today is worth knowing about before you rely on it. 4. **Create the trigger.** `POST /api/triggers` returns the trigger and its `signing_secret`. The secret is returned **only at creation** and cannot be read back. 5. **Verify each delivery.** See the [webhook delivery guide](/guides/triggers/webhook-delivery.md). Reading presets and triggers requires the `triggers:read` scope; creating, updating and deleting a trigger requires `triggers:write`. The legacy `TRIGGERS` scope grants both. A token without them is rejected with `403`, regardless of the experimental header. ## Preset inputs[​](#preset-inputs "Direct link to Preset inputs") Each entry in a preset's `inputs` array carries a `field` — the key you set in the trigger's `inputs` object — plus a `type` of `PICKLIST`, `TEXT`, or `USER`, and an `is_required` flag. Picklist inputs ship a bounded `values` array. When `is_truncated` is `true`, some selectable values were omitted: call `GET /api/trigger-presets/{preset_id}/input-values` with `field` and a `query` to narrow them. That `query` is a prefix match on the option **value**, not its display name — filter on `test.user@glean.com`, not `Test User`. The endpoint returns up to 300 values and is not cursor-paginated; it sets its own `is_truncated` when there are more. A preset may also **pin** an input to a fixed value. Supplying a pinned field is rejected with `400`. Where a preset accepts a `time_offset`, its value must be one of the offsets that preset allows. Presets are filtered to the datasources and document types the calling user can access, so two users may see different catalogs on the same deployment. ## Managing triggers[​](#managing-triggers "Direct link to Managing triggers") `PATCH /api/triggers/{trigger_id}` is a partial update — only the fields you send change. Setting `status` to `DISABLED` stops delivery while keeping the trigger and its signing secret, which is usually what you want instead of deleting and re-onboarding a new secret. `GET /api/triggers` returns the caller's own triggers, most recently updated first, and pages with `page_size` and an opaque `cursor`. Follow `next_cursor` while `has_more` is `true`; `next_cursor` is `null` on the last page. `GET /api/trigger-presets` pages the same way. Every JSON response carries a `request_id`; a successful delete returns `204` with no body. Include it when reporting a problem — it is how support correlates the call. ## Previewing events[​](#previewing-events "Direct link to Previewing events") Both search endpoints are read-only: nothing is created and no webhook is delivered. `POST /api/trigger-presets/{preset_id}/events/search` previews an unsaved preset plus inputs; `POST /api/triggers/{trigger_id}/events/search` searches recent events the trigger's current configuration matches. Three limits to plan for: * They look back **seven days**. A content-schedule preset also looks seven days *forward*, where schedules are enabled for that datasource and document type, so its results can include upcoming matches. * They return document-change events. A schedule preset is previewable, but only for the documents its filters match — the results do not replay past firings or apply the schedule offset, so they show *what* would fire, not *when*. * There is no cursor paging — `has_more` tells you results were cut off, and the only way to see more is a larger `page_size` (max 100). For error responses, see [Platform API Errors](/errors/.md) for the shared ProblemDetail code reference and remediation guidance. --- # Webhook Delivery A trigger delivers each matching event to your endpoint as a signed HTTP POST, following the [Standard Webhooks](https://www.standardwebhooks.com) specification. This guide covers verifying a delivery, authenticating Glean to your receiver, and the retry behaviour you need to design around. For the endpoints themselves, see the [Triggers API reference](/api/platform-api/triggers-overview.md). ## Verifying a delivery[​](#verifying-a-delivery "Direct link to Verifying a delivery") Every delivery carries three headers: | Header | Meaning | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `webhook-id` | Stable across retries. Use it as your idempotency key. | | `webhook-timestamp` | Unix seconds. Reject deliveries outside a tolerance window. Standard Webhooks requires a tolerance but does not prescribe one; 300 seconds is the reference libraries' default. | | `webhook-signature` | `v1,`. Glean sends a single signature today; parse the space-delimited list anyway, as Standard Webhooks allows several. | The signed string is `{webhook-id}.{webhook-timestamp}.{raw body}`, hashed with HMAC-SHA256 using the signing secret with its `whsec_` prefix removed and the remainder base64-decoded. ``` import base64, hashlib, hmac, time TOLERANCE_SECONDS = 300 def verify(headers, raw_body: bytes, signing_secret: str) -> bool: webhook_id = headers.get("webhook-id") timestamp = headers.get("webhook-timestamp") signatures = headers.get("webhook-signature") if not (webhook_id and timestamp and signatures): return False try: skew = abs(time.time() - int(timestamp)) except ValueError: return False if skew > TOLERANCE_SECONDS: return False key = base64.b64decode(signing_secret.removeprefix("whsec_")) signed = f"{webhook_id}.{timestamp}.".encode() + raw_body expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode() # The header may carry several space-delimited "v1," pairs. for part in signatures.split(): version, _, candidate = part.partition(",") if version == "v1" and hmac.compare_digest(candidate, expected): return True return False ``` Verify against the **raw** request body. Parsing the JSON and re-serializing it changes key order and whitespace, and the signature will no longer match. The signing secret is returned **only** when the trigger is created, and cannot be read back afterwards. ## Retries and idempotency[​](#retries-and-idempotency "Direct link to Retries and idempotency") Delivery is **at least once**: the same event can arrive more than once with the same `webhook-id`, so make your handling idempotent. Glean retries connection failures, timeouts, `408`, `429` and `5xx`. Other non-2xx responses, including redirects, are terminal — the event is dropped rather than retried, so return a `2xx` as soon as you have accepted it, and do slow work afterwards. ## Authenticating Glean to your endpoint[​](#authenticating-glean-to-your-endpoint "Direct link to Authenticating Glean to your endpoint") If your receiver requires its own credential, set `delivery.auth` when creating the trigger. With `type: BEARER`, Glean sends it as `Authorization: Bearer ` **in addition to** the HMAC signature — it does not replace it, and a receiver should check both. The credential is write-only: it is never returned on any read, and omitting `auth` on an update preserves the stored value. Two consequences worth designing around: * There is no in-place removal. To drop a credential, delete the trigger and create a new one. * While a credential is set, `webhook_url` cannot be changed; that update is rejected with `400`. Otherwise an editor could point the trigger at an endpoint they control and read the secret off the wire. Recreate the trigger to move it. ## Notes[​](#notes "Direct link to Notes") * Use an `https://` webhook URL. The signature proves integrity and origin, not confidentiality — the payload itself is only protected by transport security. * Deliveries are filtered per event against the subscribing user's document permissions, so a trigger never surfaces a document that user could not already open. --- # API Clients Glean offers official API clients for several popular programming languages to help developers integrate Glean's powerful search and knowledge capabilities into their applications. These clients provide language-specific interfaces to Glean's APIs, making it easier to build custom solutions without having to handle the low-level HTTP requests. info **Authentication Required**: For per-user Client API features such as search and chat, prefer an OAuth access token; the SDKs accept it through the same credential field as a Glean-issued token. Use [Client API authentication](/api-info/client/authentication/overview.md) to choose the access model. Indexing API document-management operations require [Glean-issued tokens](/api-info/indexing/authentication/overview.md). tip **Unified Libraries**: These API clients support **both Client API and Indexing API** functionality. Use the same library with different tokens depending on whether you're building user-facing features (Client API) or managing document indexing (Indexing API). ## Choose Your Language[​](#choose-your-language "Direct link to Choose Your Language") Select your preferred programming language to get started with the official Glean API client: ### [Python](/libraries/api-clients/python.md) [Full-featured Python client with framework integrations for FastAPI, Django, and Streamlit](/libraries/api-clients/python.md) ### [TypeScript](/libraries/api-clients/typescript.md) [Type-safe client for Node.js and browser applications with React, Next.js, and Express examples](/libraries/api-clients/typescript.md) ### [Go](/libraries/api-clients/go.md) [Idiomatic Go client with concurrency patterns and framework integrations for Gin and Echo](/libraries/api-clients/go.md) ### [Java](/libraries/api-clients/java.md) [Enterprise-ready Java client with Spring Boot integration and reactive programming support](/libraries/api-clients/java.md) ## What's Included[​](#whats-included "Direct link to What's Included") All official API clients provide: ### **Client API Features**[​](#client-api-features "Direct link to client-api-features") * **Chat API**: Build conversational AI applications * **Search API**: Integrate enterprise search capabilities * **Agents API**: Execute pre-built agents from Agent Builder * **Collections API**: Manage and search document collections ### **Indexing API Features**[​](#indexing-api-features "Direct link to indexing-api-features") * **Document Management**: Index, update, and delete documents * **User Management**: Manage users, groups, and permissions * **Datasource Configuration**: Set up and configure data sources * **Bulk Operations**: Efficient batch processing for large datasets ### **Developer Experience**[​](#developer-experience "Direct link to developer-experience") * **Type Safety**: Full type definitions and IntelliSense support * **Error Handling**: Structured error responses and retry mechanisms * **Framework Integration**: Examples for popular web frameworks * **Testing Support**: Mock examples and integration test patterns * **Documentation**: Comprehensive guides with real-world examples ### **Production Ready**[​](#production-ready "Direct link to production-ready") * **Rate Limiting**: Automatic handling with exponential backoff * **Timeout Control**: Configurable request timeouts * **Connection Pooling**: Efficient HTTP connection management * **Security**: Best practices for token management ## Getting Started[​](#getting-started "Direct link to Getting Started") 1. **Choose your language** from the cards above 2. **Follow the installation** instructions for your package manager 3. **Get your credentials**: * [Client API authentication](/api-info/client/authentication/overview.md) for search and chat features; OAuth is preferred for per-user integrations * [Indexing API Glean-issued tokens](/api-info/indexing/authentication/overview.md) for document management 4. **Start building** with the provided examples ## Alternative Integration Options[​](#alternative-integration-options "Direct link to Alternative Integration Options") If you prefer not to use an API client library: ### [Direct HTTP Requests](/api/client-api.md) [Use Glean's REST APIs directly with your preferred HTTP library](/api/client-api.md) ### [Web SDK](/libraries/web-sdk/overview.md) [Frontend JavaScript components for search and chat interfaces](/libraries/web-sdk/overview.md) ## Need Help?[​](#need-help "Direct link to Need Help?") ### [Client API Auth](/api-info/client/authentication/overview.md) [Authentication for search and chat](/api-info/client/authentication/overview.md) ### [Indexing API Auth](/api-info/indexing/authentication/overview.md) [Authentication for document management](/api-info/indexing/authentication/overview.md) ### [Agent Building](/guides/agents/overview.md) [Learn about building AI agents](/guides/agents/overview.md) --- # Go API Client Glean's Go API client provides idiomatic Go interfaces for integrating enterprise search and AI capabilities into your Go applications. ### [api-client-go](https://github.com/gleanwork/api-client-go) [Official Go client for Glean's Client API](https://github.com/gleanwork/api-client-go) ## Installation[​](#installation "Direct link to Installation") ``` go get github.com/gleanwork/api-client-go ``` ## Quick Start[​](#quick-start "Direct link to Quick Start") ``` package main import ( "context" "fmt" "log" "os" apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" ) func main() { ctx := context.Background() s := apiclientgo.New( apiclientgo.WithSecurity(os.Getenv("GLEAN_API_TOKEN")), apiclientgo.WithInstance(os.Getenv("GLEAN_INSTANCE")), ) res, err := s.Client.Chat.Create(ctx, components.ChatRequest{ Messages: []components.ChatMessage{ { Fragments: []components.ChatMessageFragment{ {Text: apiclientgo.Pointer("What are our company values?")}, }, }, }, }, nil, nil) if err != nil { log.Fatal(err) } if res.ChatResponse != nil { for _, msg := range res.ChatResponse.Messages { for _, frag := range msg.Fragments { if frag.Text != nil { fmt.Print(*frag.Text) } } } fmt.Println() } } ``` The unified SDK exposes both the Client and Indexing APIs from a single `*apiclientgo.Glean` value. Client-facing operations (search, chat, retrieval) live under `s.Client`; indexing operations live under `s.Indexing`. Configure the backend with either `apiclientgo.WithInstance("instance-name")` or `apiclientgo.WithServerURL("https://instance-name-be.glean.com")`. ## Core Features[​](#core-features "Direct link to Core Features") ### Chat API[​](#chat-api "Direct link to Chat API") Chat responses are returned as a list of messages, each made up of one or more fragments. Concatenate the text fragments to assemble the assistant's reply. The `chatAnswer` helper below is reused by later examples on this page. ``` package main import ( "context" "fmt" "strings" apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" ) // chatAnswer concatenates the text fragments of a chat response. func chatAnswer(resp *components.ChatResponse) string { var sb strings.Builder for _, msg := range resp.Messages { for _, frag := range msg.Fragments { if frag.Text != nil { sb.WriteString(*frag.Text) } } } return sb.String() } func chatExample(s *apiclientgo.Glean) error { ctx := context.Background() res, err := s.Client.Chat.Create(ctx, components.ChatRequest{ Messages: []components.ChatMessage{ { Fragments: []components.ChatMessageFragment{ {Text: apiclientgo.Pointer("Explain our Q4 strategy")}, }, }, }, }, nil, nil) if err != nil { return err } if res.ChatResponse != nil { fmt.Println(chatAnswer(res.ChatResponse)) } return nil } ``` To stream a response instead of waiting for the full answer, use `s.Client.Chat.CreateStream`, which returns an event stream on `res.ChatRequestStream`. ### Search API[​](#search-api "Direct link to Search API") ``` package main import ( "context" "fmt" apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" ) func searchExample(s *apiclientgo.Glean) error { ctx := context.Background() res, err := s.Client.Search.Query(ctx, components.SearchRequest{ Query: "quarterly business review", PageSize: apiclientgo.Pointer[int64](10), }, nil) if err != nil { return err } if res.SearchResponse == nil { return nil } for _, result := range res.SearchResponse.Results { title := "" if result.Title != nil { title = *result.Title } fmt.Printf("Title: %s\n", title) fmt.Printf("URL: %s\n", result.URL) } return nil } ``` `SearchResult.Title` is an optional `*string`, so guard against `nil` before dereferencing it. `SearchResult.URL` is a required `string`. ## Framework Integrations[​](#framework-integrations "Direct link to Framework Integrations") ### Gin Web Framework[​](#gin-web-framework "Direct link to Gin Web Framework") ``` package main import ( "net/http" "github.com/gin-gonic/gin" apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" ) type ChatRequest struct { Message string `json:"message"` } func setupRoutes(s *apiclientgo.Glean) *gin.Engine { r := gin.Default() r.POST("/chat", func(c *gin.Context) { var req ChatRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } res, err := s.Client.Chat.Create(c.Request.Context(), components.ChatRequest{ Messages: []components.ChatMessage{ { Fragments: []components.ChatMessageFragment{ {Text: apiclientgo.Pointer(req.Message)}, }, }, }, }, nil, nil) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } answer := "" if res.ChatResponse != nil { answer = chatAnswer(res.ChatResponse) // helper defined in the Chat API section } c.JSON(http.StatusOK, gin.H{"response": answer}) }) return r } ``` ### Echo Framework[​](#echo-framework "Direct link to Echo Framework") ``` import ( "net/http" "github.com/labstack/echo/v4" apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" ) func chatHandler(s *apiclientgo.Glean) echo.HandlerFunc { return func(c echo.Context) error { var req ChatRequest if err := c.Bind(&req); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } res, err := s.Client.Chat.Create(c.Request().Context(), components.ChatRequest{ Messages: []components.ChatMessage{ { Fragments: []components.ChatMessageFragment{ {Text: apiclientgo.Pointer(req.Message)}, }, }, }, }, nil, nil) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } answer := "" if res.ChatResponse != nil { answer = chatAnswer(res.ChatResponse) // helper defined in the Chat API section } return c.JSON(http.StatusOK, map[string]string{ "response": answer, }) } } ``` ## Concurrency Patterns[​](#concurrency-patterns "Direct link to Concurrency Patterns") ### Batch Processing with Goroutines[​](#batch-processing-with-goroutines "Direct link to Batch Processing with Goroutines") ``` func batchSearch(s *apiclientgo.Glean, queries []string) ([]*components.SearchResponse, error) { ctx := context.Background() results := make([]*components.SearchResponse, len(queries)) errs := make([]error, len(queries)) var wg sync.WaitGroup for i, query := range queries { wg.Add(1) go func(index int, q string) { defer wg.Done() res, err := s.Client.Search.Query(ctx, components.SearchRequest{ Query: q, }, nil) if err != nil { errs[index] = err return } results[index] = res.SearchResponse }(i, query) } wg.Wait() // Check for errors for _, err := range errs { if err != nil { return nil, err } } return results, nil } ``` ## Authentication[​](#authentication "Direct link to Authentication") ### User-Scoped Tokens[​](#user-scoped-tokens "Direct link to User-Scoped Tokens") The API token is supplied through `apiclientgo.WithSecurity`: ``` s := apiclientgo.New( apiclientgo.WithSecurity("your-user-token"), apiclientgo.WithServerURL("https://instance-name-be.glean.com"), ) ``` ### Global Tokens with ActAs[​](#global-tokens-with-actas "Direct link to Global Tokens with ActAs") Per-request headers are passed with `operations.WithSetHeaders`, supplied as trailing options after the request's optional `locale` and `timezoneOffset` parameters: ``` import ( apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" "github.com/gleanwork/api-client-go/models/operations" ) res, err := s.Client.Chat.Create(ctx, components.ChatRequest{ Messages: []components.ChatMessage{ { Fragments: []components.ChatMessageFragment{ {Text: apiclientgo.Pointer("Hello")}, }, }, }, }, nil, nil, operations.WithSetHeaders(map[string]string{ "X-Glean-ActAs": "user@company.com", })) ``` ### OAuth Access Tokens[​](#oauth-access-tokens "Direct link to OAuth Access Tokens") An OAuth access token is a bearer credential, so it goes in the same `WithSecurity` option: ``` s := apiclientgo.New( apiclientgo.WithSecurity(oauthAccessToken), apiclientgo.WithServerURL("https://instance-name-be.glean.com"), ) ``` Tokens issued by the **Glean OAuth Authorization Server** (including tokens obtained via Dynamic Client Registration) are detected automatically. Tokens issued by an **external identity provider** (Google, Okta, Azure, etc.) additionally require the `X-Glean-Auth-Type: OAUTH` header on each request: ``` import ( apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" "github.com/gleanwork/api-client-go/models/operations" ) res, err := s.Client.Search.Query(ctx, components.SearchRequest{ Query: "quarterly reports", }, nil, operations.WithSetHeaders(map[string]string{ "X-Glean-Auth-Type": "OAUTH", })) ``` See the [OAuth authentication guide](/api-info/client/authentication/oauth.md) for identity-provider setup. #### Complete Example: Authorization Code with PKCE[​](#complete-example-authorization-code-with-pkce "Direct link to Complete Example: Authorization Code with PKCE") This example uses [`golang.org/x/oauth2`](https://pkg.go.dev/golang.org/x/oauth2). It runs the Authorization Code flow with PKCE (`S256ChallengeOption` / `VerifierOption`) and `AccessTypeOffline` for a refresh token, then passes the access token to the Glean client. Read `AuthURL`/`TokenURL` from the issuer's metadata document. ``` package main import ( "encoding/json" "net/http" "os" "golang.org/x/oauth2" apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" "github.com/gleanwork/api-client-go/models/operations" ) var ( conf = &oauth2.Config{ ClientID: os.Getenv("OAUTH_CLIENT_ID"), ClientSecret: os.Getenv("OAUTH_CLIENT_SECRET"), // omit for a public client RedirectURL: "http://localhost:8080/callback", Scopes: []string{"openid", "offline_access", "SEARCH"}, // SEARCH lets the token call /search; offline_access → refresh token Endpoint: oauth2.Endpoint{ AuthURL: os.Getenv("OAUTH_AUTH_URL"), TokenURL: os.Getenv("OAUTH_TOKEN_URL"), }, } // Demo only: generate and store the verifier per request (e.g. in session) in production. verifier = oauth2.GenerateVerifier() ) func main() { http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline, oauth2.S256ChallengeOption(verifier)) http.Redirect(w, r, url, http.StatusFound) }) http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() tok, err := conf.Exchange(ctx, r.URL.Query().Get("code"), oauth2.VerifierOption(verifier)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } s := apiclientgo.New( apiclientgo.WithSecurity(tok.AccessToken), apiclientgo.WithServerURL(os.Getenv("GLEAN_SERVER_URL")), ) res, err := s.Client.Search.Query(ctx, components.SearchRequest{ Query: "quarterly reports", }, nil, // Omit this option when the token is from the Glean Authorization Server. operations.WithSetHeaders(map[string]string{"X-Glean-Auth-Type": "OAUTH"})) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(res.SearchResponse) }) http.ListenAndServe(":8080", nil) } ``` tip Access tokens expire. Wrap the token in `conf.TokenSource(ctx, tok)` to refresh automatically when you requested `offline_access`. ## Error Handling[​](#error-handling "Direct link to Error Handling") Every operation returns either a response or an error, never both. API errors are returned as `apierrors.APIError`, which carries the HTTP status code and response body. Some operations return more specific typed errors (for example, `Search.Query` may return `apierrors.GleanDataError` for `403`/`422`); use `errors.As` to detect them. ``` package main import ( "context" "errors" "fmt" "time" apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/apierrors" "github.com/gleanwork/api-client-go/models/components" ) func safeChat(s *apiclientgo.Glean, message string) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() res, err := s.Client.Chat.Create(ctx, components.ChatRequest{ Messages: []components.ChatMessage{ { Fragments: []components.ChatMessageFragment{ {Text: apiclientgo.Pointer(message)}, }, }, }, }, nil, nil) if err != nil { var apiErr *apierrors.APIError if errors.As(err, &apiErr) { return "", fmt.Errorf("chat failed with status %d: %w", apiErr.StatusCode, apiErr) } return "", fmt.Errorf("chat error: %w", err) } if res.ChatResponse == nil { return "", nil } return chatAnswer(res.ChatResponse), nil // helper defined in the Chat API section } ``` ## Testing[​](#testing "Direct link to Testing") The SDK talks to a standard `net/http` client, so the simplest way to test code that calls Glean is to point the client at an [`httptest`](https://pkg.go.dev/net/http/httptest) server via `WithServerURL` and return canned responses: ``` package main import ( "context" "net/http" "net/http/httptest" "testing" apiclientgo "github.com/gleanwork/api-client-go" "github.com/gleanwork/api-client-go/models/components" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestSearch(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"results":[{"title":"Q4 Report","url":"https://example.com/q4"}]}`)) })) defer srv.Close() s := apiclientgo.New( apiclientgo.WithSecurity("test-token"), apiclientgo.WithServerURL(srv.URL), ) res, err := s.Client.Search.Query(context.Background(), components.SearchRequest{ Query: "quarterly business review", }, nil) require.NoError(t, err) require.NotNil(t, res.SearchResponse) require.Len(t, res.SearchResponse.Results, 1) assert.Equal(t, "https://example.com/q4", res.SearchResponse.Results[0].URL) } ``` For lower-level control (custom timeouts, transports, or request assertions) you can also supply your own `*http.Client` with `apiclientgo.WithClient`. ## Additional Resources[​](#additional-resources "Direct link to Additional Resources") ### [GitHub Repository](https://github.com/gleanwork/api-client-go) [Source code and examples](https://github.com/gleanwork/api-client-go) ### [API Reference](/api/client-api.md) [Complete endpoint documentation](/api/client-api.md) ### [Authentication Guide](/api-info/client/authentication/overview.md) [Token creation and management](/api-info/client/authentication/overview.md) ### [Go Documentation](https://pkg.go.dev/github.com/gleanwork/api-client-go) [Go package documentation](https://pkg.go.dev/github.com/gleanwork/api-client-go) --- # Java API Client Glean's Java API client provides enterprise-ready integration for Java applications, with support for Spring Boot and traditional enterprise patterns. ### [glean-api-client](https://github.com/gleanwork/api-client-java) [Official Java client for Glean's Client API](https://github.com/gleanwork/api-client-java) ## Installation[​](#installation "Direct link to Installation") * Maven * Gradle ``` com.glean.api-client glean-api-client 0.x.x ``` ``` implementation 'com.glean.api-client:glean-api-client:0.x.x' ``` ## Quick Start[​](#quick-start "Direct link to Quick Start") ``` import com.glean.api_client.glean_api_client.Glean; import com.glean.api_client.glean_api_client.models.components.*; import java.util.List; public class GleanExample { public static void main(String[] args) throws Exception { Glean client = Glean.builder() .apiToken(System.getenv("GLEAN_API_TOKEN")) .serverURL(System.getenv("GLEAN_SERVER_URL")) .build(); var response = client.client().chat().create() .chatRequest(ChatRequest.builder() .messages(List.of( ChatMessage.builder() .fragments(List.of( ChatMessageFragment.builder() .text("What are our company values?") .build())) .build())) .build()) .call(); if (response.chatResponse().isPresent()) { System.out.println(response.chatResponse().get()); } } } ``` ## Core Features[​](#core-features "Direct link to Core Features") ### Chat API[​](#chat-api "Direct link to Chat API") ``` public class ChatService { private final Glean client; public ChatService(String apiToken, String serverURL) { this.client = Glean.builder() .apiToken(apiToken) .serverURL(serverURL) .build(); } public String sendMessage(String message) throws Exception { var response = client.client().chat().create() .chatRequest(ChatRequest.builder() .messages(List.of( ChatMessage.builder() .fragments(List.of( ChatMessageFragment.builder() .text(message) .build())) .build())) .build()) .call(); return response.chatResponse() .map(chat -> chat.messages().stream() .flatMap(msg -> msg.fragments().stream()) .map(fragment -> fragment.text().orElse("")) .collect(Collectors.joining())) .orElse("No response"); } } ``` ### Search API[​](#search-api "Direct link to Search API") ``` public class SearchService { private final Glean client; public SearchService(String apiToken, String serverURL) { this.client = Glean.builder() .apiToken(apiToken) .serverURL(serverURL) .build(); } public List search(String query) throws Exception { var response = client.client().search().query() .searchRequest(SearchRequest.builder() .query(query) .pageSize(10L) .build()) .call(); return response.searchResponse() .map(SearchResponse::results) .orElse(List.of()); } } ``` ## Spring Boot Integration[​](#spring-boot-integration "Direct link to Spring Boot Integration") ### Configuration[​](#configuration "Direct link to Configuration") ``` @Configuration @ConfigurationProperties(prefix = "glean") public class GleanConfig { private String apiToken; private String serverURL; // Getters and setters public String getApiToken() { return apiToken; } public void setApiToken(String apiToken) { this.apiToken = apiToken; } public String getServerURL() { return serverURL; } public void setServerURL(String serverURL) { this.serverURL = serverURL; } } @Configuration @EnableConfigurationProperties(GleanConfig.class) public class GleanAutoConfiguration { @Bean public Glean gleanClient(GleanConfig config) { return Glean.builder() .apiToken(config.getApiToken()) .serverURL(config.getServerURL()) .build(); } } ``` ### REST Controller[​](#rest-controller "Direct link to REST Controller") ``` @RestController @RequestMapping("/api") public class GleanController { private final Glean gleanClient; public GleanController(Glean gleanClient) { this.gleanClient = gleanClient; } @PostMapping("/chat") public ResponseEntity chat(@RequestBody Map request) { try { String message = request.get("message"); var response = gleanClient.client().chat().create() .chatRequest(ChatRequest.builder() .messages(List.of( ChatMessage.builder() .fragments(List.of( ChatMessageFragment.builder() .text(message) .build())) .build())) .build()) .call(); String responseText = response.chatResponse() .map(chat -> chat.messages().stream() .flatMap(msg -> msg.fragments().stream()) .map(fragment -> fragment.text().orElse("")) .collect(Collectors.joining())) .orElse("No response"); return ResponseEntity.ok(responseText); } catch (Exception e) { return ResponseEntity.status(500).body("Error: " + e.getMessage()); } } } ``` ## Authentication[​](#authentication "Direct link to Authentication") ### User-Scoped Tokens[​](#user-scoped-tokens "Direct link to User-Scoped Tokens") ``` Glean client = Glean.builder() .apiToken("your-user-token") .serverURL("https://your-server-id-be.glean.com") .build(); ``` ### Configuration Properties[​](#configuration-properties "Direct link to Configuration Properties") ``` # application.yml glean: api-token: ${GLEAN_API_TOKEN} server-url: ${GLEAN_SERVER_URL} ``` ### OAuth Access Tokens[​](#oauth-access-tokens "Direct link to OAuth Access Tokens") An OAuth access token is a bearer credential, so it goes in the same `apiToken` field: ``` Glean client = Glean.builder() .apiToken(oauthAccessToken) .serverURL("https://your-server-id-be.glean.com") .build(); ``` Tokens issued by the **Glean OAuth Authorization Server** (including tokens obtained via Dynamic Client Registration) are detected automatically and need no extra header. Tokens issued by an **external identity provider** (Google, Okta, Azure, etc.) additionally require the `X-Glean-Auth-Type: OAUTH` header on each request. The builder has no per-request header option, so supply a custom HTTP client that adds it: ``` import com.glean.api_client.glean_api_client.utils.HTTPClient; import com.glean.api_client.glean_api_client.utils.SpeakeasyHTTPClient; import com.glean.api_client.glean_api_client.utils.Utils; import java.io.InputStream; import java.net.http.HttpRequest; import java.net.http.HttpResponse; HTTPClient oauthClient = new SpeakeasyHTTPClient() { @Override public HttpResponse send(HttpRequest request) throws java.io.IOException, InterruptedException, java.net.URISyntaxException { return super.send(Utils.copy(request) .header("X-Glean-Auth-Type", "OAUTH") .build()); } }; Glean client = Glean.builder() .apiToken(oauthAccessToken) .serverURL("https://your-server-id-be.glean.com") .client(oauthClient) .build(); ``` See the [OAuth authentication guide](/api-info/client/authentication/oauth.md) for identity-provider setup. #### Complete Example: Authorization Code with PKCE[​](#complete-example-authorization-code-with-pkce "Direct link to Complete Example: Authorization Code with PKCE") This example uses Spring Security's OAuth2 Client. Point `OAUTH_ISSUER` at the Glean OAuth Authorization Server or your IdP. For a confidential client, enable PKCE with an authorization-request customizer (it is on by default for public clients). ``` # application.yml spring: security: oauth2: client: registration: glean: provider: glean client-id: ${OAUTH_CLIENT_ID} client-secret: ${OAUTH_CLIENT_SECRET} authorization-grant-type: authorization_code scope: openid,offline_access,SEARCH # SEARCH lets the token call /search; offline_access requests a refresh token redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}' provider: glean: issuer-uri: ${OAUTH_ISSUER} glean: server-url: ${GLEAN_SERVER_URL} ``` ``` // SecurityConfig.java — enable PKCE for a confidential client import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestCustomizers; import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http, ClientRegistrationRepository repo) throws Exception { var resolver = new DefaultOAuth2AuthorizationRequestResolver(repo, "/oauth2/authorization"); resolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()); http.oauth2Login(login -> login .authorizationEndpoint(endpoint -> endpoint.authorizationRequestResolver(resolver))); return http.build(); } } ``` ``` // SearchController.java — exchange the authorized client's token for a Glean call import org.springframework.beans.factory.annotation.Value; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.glean.api_client.glean_api_client.Glean; import com.glean.api_client.glean_api_client.models.components.*; import java.util.List; @RestController public class SearchController { @Value("${glean.server-url}") private String serverURL; @GetMapping("/search") public List search( @RegisteredOAuth2AuthorizedClient("glean") OAuth2AuthorizedClient authorizedClient) throws Exception { String accessToken = authorizedClient.getAccessToken().getTokenValue(); // For external-IdP tokens, add a custom HTTPClient that sets // X-Glean-Auth-Type: OAUTH (see "OAuth Access Tokens" above). Glean // Authorization Server tokens need no extra header. Glean glean = Glean.builder() .apiToken(accessToken) .serverURL(serverURL) .build(); return glean.client().search().query() .searchRequest(SearchRequest.builder() .query("quarterly reports") .pageSize(10L) .build()) .call() .searchResponse() .map(SearchResponse::results) .orElse(List.of()); } } ``` ## Error Handling[​](#error-handling "Direct link to Error Handling") ``` public String safeChat(Glean client, String message) { try { var response = client.client().chat().create() .chatRequest(ChatRequest.builder() .messages(List.of( ChatMessage.builder() .fragments(List.of( ChatMessageFragment.builder() .text(message) .build())) .build())) .build()) .call(); return response.chatResponse() .map(chat -> chat.messages().stream() .flatMap(msg -> msg.fragments().stream()) .map(fragment -> fragment.text().orElse("")) .collect(Collectors.joining())) .orElse("No response"); } catch (Exception e) { System.err.println("API error: " + e.getMessage()); return "Sorry, I couldn't process your request."; } } ``` ## Testing[​](#testing "Direct link to Testing") ### JUnit 5 with Mockito[​](#junit-5-with-mockito "Direct link to JUnit 5 with Mockito") ``` @ExtendWith(MockitoExtension.class) class ChatServiceTest { @Mock private Glean gleanClient; @InjectMocks private ChatService chatService; @Test void shouldSendMessageSuccessfully() { String message = "Test message"; String expectedResponse = "Test response"; // Mock setup and test implementation assertNotNull(expectedResponse); } } ``` ## Additional Resources[​](#additional-resources "Direct link to Additional Resources") ### [GitHub Repository](https://github.com/gleanwork/api-client-java) [Source code and examples](https://github.com/gleanwork/api-client-java) ### [API Reference](/api/client-api.md) [Complete endpoint documentation](/api/client-api.md) ### [Authentication Guide](/api-info/client/authentication/overview.md) [Token creation and management](/api-info/client/authentication/overview.md) ### [Maven Central](https://central.sonatype.com/) [Maven repository](https://central.sonatype.com/) --- # Python API Client Glean's Python API client provides a Pythonic interface to Glean's Client API, making it easy to integrate enterprise search and AI capabilities into your Python applications. ### [glean-api-client](https://github.com/gleanwork/api-client-python) [Official Python client for Glean's Client API](https://github.com/gleanwork/api-client-python) info **Authentication Required**: You'll need a [Client API token](/api-info/client/authentication/overview.md) to use this library. ## Installation[​](#installation "Direct link to Installation") * pip * poetry * uv ``` pip install glean-api-client ``` ``` poetry add glean-api-client ``` ``` uv add glean-api-client ``` ## Quick Start[​](#quick-start "Direct link to Quick Start") 1 Set up environment variables ``` export GLEAN_SERVER_URL="https://your-server-id-be.glean.com" export GLEAN_API_TOKEN="your-token-here" ``` 2 Basic usage ``` from glean.api_client import Glean import os with Glean( api_token=os.getenv("GLEAN_API_TOKEN"), server_url=os.getenv("GLEAN_SERVER_URL"), ) as client: response = client.client.chat.create( messages=[{ "fragments": [{"text": "What are our company values?"}] }], timeout_millis=30000 ) print(response) ``` ## Core Features[​](#core-features "Direct link to Core Features") ### Chat API[​](#chat-api "Direct link to Chat API") Build conversational AI applications: ``` # Simple chat response = client.client.chat.create( messages=[{"fragments": [{"text": "Explain our Q4 strategy"}]}] ) # Streaming chat: create_stream returns the streamed response as text response = client.client.chat.create_stream( messages=[{"fragments": [{"text": "What are our priorities?"}]}] ) print(response) ``` ### Search API[​](#search-api "Direct link to Search API") Integrate enterprise search: ``` results = client.client.search.query( query="quarterly business review", page_size=10 ) for result in results.results: print(f"Title: {result.title}") print(f"URL: {result.url}") ``` ### Agents API[​](#agents-api "Direct link to Agents API") Execute pre-built agents: ``` response = client.client.agents.run( agent_id="your-agent-id", input={"query": "Analyze sales performance"} ) ``` ## Framework Integrations[​](#framework-integrations "Direct link to Framework Integrations") ### FastAPI[​](#fastapi "Direct link to FastAPI") ``` from fastapi import FastAPI from glean.api_client import Glean from pydantic import BaseModel app = FastAPI() class ChatRequest(BaseModel): message: str @app.post("/chat") async def chat_endpoint(request: ChatRequest): with Glean( api_token=os.getenv("GLEAN_API_TOKEN"), server_url=os.getenv("GLEAN_SERVER_URL"), ) as client: response = client.client.chat.create( messages=[{"fragments": [{"text": request.message}]}] ) return {"response": str(response)} ``` ### Django[​](#django "Direct link to Django") ``` from django.http import JsonResponse from glean.api_client import Glean import json def chat_view(request): data = json.loads(request.body) message = data.get('message') with Glean( api_token=os.getenv("GLEAN_API_TOKEN"), server_url=os.getenv("GLEAN_SERVER_URL"), ) as client: response = client.client.chat.create( messages=[{"fragments": [{"text": message}]}] ) return JsonResponse({'response': str(response)}) ``` ### Streamlit[​](#streamlit "Direct link to Streamlit") ``` import streamlit as st from glean.api_client import Glean st.title("Company Knowledge Assistant") user_input = st.text_input("Ask a question:") if user_input: with Glean( api_token=os.getenv("GLEAN_API_TOKEN"), server_url=os.getenv("GLEAN_SERVER_URL"), ) as client: response = client.client.chat.create( messages=[{"fragments": [{"text": user_input}]}] ) st.write(response) ``` ## Authentication[​](#authentication "Direct link to Authentication") ### User-Scoped Tokens[​](#user-scoped-tokens "Direct link to User-Scoped Tokens") ``` client = Glean( api_token="your-user-token", server_url="https://your-server-id-be.glean.com" ) ``` ### Global Tokens with ActAs[​](#global-tokens-with-actas "Direct link to Global Tokens with ActAs") ``` response = client.client.chat.create( messages=[{"fragments": [{"text": "Hello"}]}], http_headers={"X-Glean-ActAs": "user@company.com"} ) ``` ### OAuth Access Tokens[​](#oauth-access-tokens "Direct link to OAuth Access Tokens") An OAuth access token is a bearer credential, so it goes in the same `api_token` field: ``` client = Glean( api_token=oauth_access_token, server_url="https://your-server-id-be.glean.com", ) ``` Tokens issued by the **Glean OAuth Authorization Server** (including tokens obtained via Dynamic Client Registration) are detected automatically. Tokens issued by an **external identity provider** (Google, Okta, Azure, etc.) additionally require the `X-Glean-Auth-Type: OAUTH` header on each request: ``` response = client.client.chat.create( messages=[{"fragments": [{"text": "Hello"}]}], http_headers={"X-Glean-Auth-Type": "OAUTH"} ) ``` See the [OAuth authentication guide](/api-info/client/authentication/oauth.md) for identity-provider setup. #### Complete Example: Authorization Code with PKCE[​](#complete-example-authorization-code-with-pkce "Direct link to Complete Example: Authorization Code with PKCE") This example uses [Authlib](https://docs.authlib.org/) with Flask. Setting `code_challenge_method` enables PKCE; Authlib stores the verifier and state in the session and verifies them on the callback. Point `OAUTH_METADATA_URL` at the Glean OAuth Authorization Server metadata (`https://your-server-id-be.glean.com/.well-known/oauth-authorization-server`) or your IdP's discovery document. ``` import os from flask import Flask, jsonify, url_for from authlib.integrations.flask_client import OAuth from glean.api_client import Glean app = Flask(__name__) app.secret_key = os.urandom(24) oauth = OAuth(app) oauth.register( name="glean", client_id=os.environ["OAUTH_CLIENT_ID"], client_secret=os.environ.get("OAUTH_CLIENT_SECRET"), # omit for a public client server_metadata_url=os.environ["OAUTH_METADATA_URL"], client_kwargs={ "scope": "openid offline_access SEARCH", # SEARCH lets the token call /search (a Glean scope); offline_access requests a refresh token "code_challenge_method": "S256", # enable PKCE }, ) @app.route("/login") def login(): return oauth.glean.authorize_redirect(url_for("callback", _external=True)) @app.route("/callback") def callback(): token = oauth.glean.authorize_access_token() # verifies state + PKCE, exchanges code with Glean( api_token=token["access_token"], server_url=os.environ["GLEAN_SERVER_URL"], ) as glean: results = glean.client.search.query( query="quarterly reports", page_size=10, # Omit this header when the token is from the Glean Authorization Server. http_headers={"X-Glean-Auth-Type": "OAUTH"}, ) titles = [r.title for r in (results.results or [])] return jsonify({"titles": titles}) if __name__ == "__main__": app.run(port=5000) ``` tip Access tokens expire. The `token` dict includes `refresh_token` when you request the `offline_access` scope; use it to obtain new access tokens before expiry. ## Error Handling[​](#error-handling "Direct link to Error Handling") ``` from glean.api_client import Glean, errors import os with Glean( api_token=os.getenv("GLEAN_API_TOKEN"), server_url=os.getenv("GLEAN_SERVER_URL"), ) as client: try: response = client.client.search.query( query="quarterly business review", page_size=10, ) print(response) except errors.GleanDataError as e: # 403 Permission Denied, 422 Invalid Query print(f"Data error: {e}") except errors.GleanError as e: # Base error for all other API errors (400, 401, 408, 429, 5XX) print(f"API error: {e}") ``` ## Testing[​](#testing "Direct link to Testing") ### Unit Testing with Mocks[​](#unit-testing-with-mocks "Direct link to Unit Testing with Mocks") ``` import pytest from unittest.mock import patch, MagicMock @pytest.fixture def mock_glean_client(): with patch('your_app.Glean') as mock: client_instance = MagicMock() mock.return_value.__enter__.return_value = client_instance yield client_instance def test_chat_service(mock_glean_client): mock_response = MagicMock() mock_response.text = "Test response" mock_glean_client.client.chat.create.return_value = mock_response result = send_message("Hello") assert result == "Test response" ``` ## Additional Resources[​](#additional-resources "Direct link to Additional Resources") ### [GitHub Repository](https://github.com/gleanwork/api-client-python) [Source code and examples](https://github.com/gleanwork/api-client-python) ### [API Reference](/api/client-api.md) [Complete endpoint documentation](/api/client-api.md) ### [Authentication Guide](/api-info/client/authentication/overview.md) [Token creation and management](/api-info/client/authentication/overview.md) ### [LangChain Integration](/guides/agents/langchain.md) [Use with LangChain framework](/guides/agents/langchain.md) --- # TypeScript API Client Glean's TypeScript API client provides full type safety for integrating Glean's search and AI capabilities into web applications and Node.js services. ### [@gleanwork/api-client](https://github.com/gleanwork/api-client-typescript) [Official TypeScript/JavaScript client for Glean's Client API](https://github.com/gleanwork/api-client-typescript) ## Installation[​](#installation "Direct link to Installation") * npm * yarn * pnpm ``` npm install @gleanwork/api-client ``` ``` yarn add @gleanwork/api-client ``` ``` pnpm add @gleanwork/api-client ``` ## Quick Start[​](#quick-start "Direct link to Quick Start") ``` import { Glean } from "@gleanwork/api-client"; const client = new Glean({ apiToken: process.env.GLEAN_API_TOKEN, serverURL: process.env.GLEAN_SERVER_URL, }); const result = await client.client.chat.create({ messages: [{ fragments: [{ text: "What are our company values?" }] }] }); ``` ## Core Features[​](#core-features "Direct link to Core Features") ### Chat API[​](#chat-api "Direct link to Chat API") ``` // Simple chat const response = await client.client.chat.create({ messages: [{ fragments: [{ text: "Explain our Q4 strategy" }] }] }); // Streaming responses const stream = await client.client.chat.createStream({ messages: [{ fragments: [{ text: "What are our priorities?" }] }] }); console.log(stream); ``` ### Search API[​](#search-api "Direct link to Search API") ``` const results = await client.client.search.query({ query: "quarterly business review", pageSize: 10 }); results.results?.forEach(result => { console.log(`Title: ${result.title}`); console.log(`URL: ${result.url}`); }); ``` ## Framework Integrations[​](#framework-integrations "Direct link to Framework Integrations") ### Next.js API Route[​](#nextjs-api-route "Direct link to Next.js API Route") ``` // app/api/chat/route.ts import { NextRequest, NextResponse } from 'next/server'; import { Glean } from '@gleanwork/api-client'; export async function POST(request: NextRequest) { const { message } = await request.json(); const client = new Glean({ apiToken: process.env.GLEAN_API_TOKEN!, serverURL: process.env.GLEAN_SERVER_URL!, }); const response = await client.client.chat.create({ messages: [{ fragments: [{ text: message }] }] }); const answer = response.messages ?.flatMap((m) => m.fragments ?? []) .map((f) => f.text ?? '') .join('') ?? ''; return NextResponse.json({ answer }); } ``` ### React Component[​](#react-component "Direct link to React Component") ``` import React, { useState } from 'react'; import { Glean } from '@gleanwork/api-client'; type ChatComponentProps = { apiToken: string; serverURL: string; }; export function ChatComponent({ apiToken, serverURL, }: ChatComponentProps) { const [input, setInput] = useState(''); const [response, setResponse] = useState(''); const client = new Glean({ apiToken, serverURL }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const result = await client.client.chat.create({ messages: [{ fragments: [{ text: input }] }], }); const answer = result.messages ?.flatMap((m) => m.fragments ?? []) .map((f) => f.text ?? '') .join('') ?? ''; setResponse(answer); }; return (
) => setInput(e.target.value) } placeholder="Ask a question..." /> {response &&
{response}
}
); } ``` ### Express.js[​](#expressjs "Direct link to Express.js") ``` import express from 'express'; import { Glean } from '@gleanwork/api-client'; const app = express(); app.use(express.json()); const client = new Glean({ apiToken: process.env.GLEAN_API_TOKEN!, serverURL: process.env.GLEAN_SERVER_URL!, }); app.post('/api/chat', async (req, res) => { const { message } = req.body; const response = await client.client.chat.create({ messages: [{ fragments: [{ text: message }] }] }); const answer = response.messages ?.flatMap((m) => m.fragments ?? []) .map((f) => f.text ?? '') .join('') ?? ''; res.json({ answer }); }); ``` ## Authentication[​](#authentication "Direct link to Authentication") ### User-Scoped Tokens[​](#user-scoped-tokens "Direct link to User-Scoped Tokens") ``` const client = new Glean({ apiToken: "your-user-token", serverURL: "https://your-server-id-be.glean.com" }); ``` ### Global Tokens with ActAs[​](#global-tokens-with-actas "Direct link to Global Tokens with ActAs") ``` const response = await client.client.chat.create( { messages: [{ fragments: [{ text: "Hello" }] }] }, undefined, undefined, { fetchOptions: { headers: { "X-Glean-ActAs": "user@company.com" } } } ); ``` ### OAuth Access Tokens[​](#oauth-access-tokens "Direct link to OAuth Access Tokens") An OAuth access token is a bearer credential, so it goes in the same `apiToken` field — no separate option is needed: ``` const client = new Glean({ apiToken: oauthAccessToken, serverURL: "https://your-server-id-be.glean.com", }); ``` Tokens issued by the **Glean OAuth Authorization Server** (including tokens obtained via Dynamic Client Registration) are detected automatically. Tokens issued by an **external identity provider** (Google, Okta, Azure, etc.) additionally require the `X-Glean-Auth-Type: OAUTH` header on each request: ``` const response = await client.client.chat.create( { messages: [{ fragments: [{ text: "Hello" }] }] }, undefined, undefined, { fetchOptions: { headers: { "X-Glean-Auth-Type": "OAUTH" } } }, ); ``` See the [OAuth authentication guide](/api-info/client/authentication/oauth.md) for identity-provider setup. #### Complete Example: Authorization Code with PKCE[​](#complete-example-authorization-code-with-pkce "Direct link to Complete Example: Authorization Code with PKCE") This example uses [`openid-client`](https://github.com/panva/openid-client) (v6) with Express. It runs the Authorization Code flow with PKCE, then passes the resulting access token to the Glean client. Point `OAUTH_ISSUER` at the Glean OAuth Authorization Server metadata (`https://your-server-id-be.glean.com/.well-known/oauth-authorization-server`) or your IdP's issuer. ``` import express from 'express'; import session from 'express-session'; import * as client from 'openid-client'; import { Glean } from '@gleanwork/api-client'; const app = express(); app.use(session({ secret: 'change-me', resave: false, saveUninitialized: false })); let config: client.Configuration; async function init() { config = await client.discovery( new URL(process.env.OAUTH_ISSUER!), process.env.OAUTH_CLIENT_ID!, process.env.OAUTH_CLIENT_SECRET, // omit for a public client ); } app.get('/login', async (req, res) => { // PKCE values must be generated per request and stored in the session. const codeVerifier = client.randomPKCECodeVerifier(); const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier); (req.session as any).codeVerifier = codeVerifier; const params: Record = { redirect_uri: 'http://localhost:3000/callback', scope: 'openid offline_access SEARCH', // SEARCH lets the token call /search (a Glean scope); offline_access requests a refresh token code_challenge: codeChallenge, code_challenge_method: 'S256', }; // state is only needed if the server doesn't advertise PKCE support if (!config.serverMetadata().supportsPKCE()) { const state = client.randomState(); (req.session as any).state = state; params.state = state; } res.redirect(client.buildAuthorizationUrl(config, params).href); }); app.get('/callback', async (req, res) => { const currentUrl = new URL(req.url, `http://${req.headers.host}`); const tokens = await client.authorizationCodeGrant(config, currentUrl, { pkceCodeVerifier: (req.session as any).codeVerifier, expectedState: (req.session as any).state, // undefined when PKCE is used }); const glean = new Glean({ apiToken: tokens.access_token, serverURL: process.env.GLEAN_SERVER_URL!, }); const results = await glean.client.search.query( { query: 'quarterly reports', pageSize: 10 }, undefined, // Omit these headers when the token is from the Glean Authorization Server. { fetchOptions: { headers: { 'X-Glean-Auth-Type': 'OAUTH' } } }, ); res.json(results); }); init().then(() => app.listen(3000)); ``` tip Access tokens expire. Persist `tokens.refresh_token` and refresh with `client.refreshTokenGrant(config, refreshToken)` before expiry. ## Error Handling[​](#error-handling "Direct link to Error Handling") ``` import { GleanError, GleanDataError } from "@gleanwork/api-client/models/errors"; try { const response = await client.client.chat.create({ messages: [{ fragments: [{ text: "Hello" }] }] }); } catch (error) { if (error instanceof GleanError) { console.error(error.message); console.error(error.statusCode); console.error(error.body); } if (error instanceof GleanDataError) { console.error(error.errorMessages); } throw error; } ``` ## Testing[​](#testing "Direct link to Testing") ``` import { jest } from '@jest/globals'; import { Glean } from '@gleanwork/api-client'; jest.mock('@gleanwork/api-client'); const MockedGlean = Glean as jest.MockedClass; test('chat service', async () => { const mockCreate = jest.fn().mockResolvedValue({ messages: [{ fragments: [{ text: 'Test response' }] }] }); MockedGlean.mockImplementation(() => ({ client: { chat: { create: mockCreate } } } as any)); // Test your code here }); ``` ## Additional Resources[​](#additional-resources "Direct link to Additional Resources") ### [GitHub Repository](https://github.com/gleanwork/api-client-typescript) [Source code and examples](https://github.com/gleanwork/api-client-typescript) ### [API Reference](/api/client-api.md) [Complete endpoint documentation](/api/client-api.md) ### [Web SDK](/libraries/web-sdk/overview.md) [Frontend integration components](/libraries/web-sdk/overview.md) ### [Authentication Guide](/api-info/client/authentication/overview.md) [Token creation and management](/api-info/client/authentication/overview.md) --- # Indexing SDK Indexing SDK # Describe your source. Get a connector. Install the Connector Builder plugin and tell your coding agent what you want indexed. It explores the source's API, plans the connector with you, generates it against this SDK, and tests it before anything reaches your index. Install the plugin[Write it by hand](/libraries/indexing-sdk/quickstart.md) terminalConnector Builder ``` $ claude plugin marketplace add gleanwork/glean-indexing-sdk ✔ Added marketplace: glean-indexing-sdk $ claude plugin install glean-connector-builder@glean-indexing-sdk ✔ Installed: glean-connector-builder > Build a connector for our internal wiki at wiki.acme.com. It has a REST API with cursor pagination and per-page ACLs. ● Reading the wiki API docs ● Confirmed scope: 12,400 pages, 3 permission groups ● Generating connector.py, data_client.py, tests ● Phase 1 tests: 8 passed, 0 failed ● Ready to index — run it when you are ``` ## Install the Connector Builder Point your agent's plugin host at the SDK repository, which doubles as the marketplace. Claude CodeCodexCursor terminalClaude Code ``` claude plugin marketplace add gleanwork/glean-indexing-sdk claude plugin install glean-connector-builder@glean-indexing-sdk ``` Run /reload-plugins to pick it up without restarting.Invoke explicitly with `/glean-connector-builder:connector-builder`, or just describe your source. ### Then describe your source I want to push my Webex data to Glean. Build a connector for me. Build a connector for our internal wiki at wiki.acme.com. It has a REST API with cursor pagination, and each page carries a list of groups that can read it. The agent asks for whatever scope it still needs, confirms a plan with you, generates the connector, and runs the mocked and recorded test phases before offering to index anything. Hosts pull from the repository, so updating is a marketplace refresh rather than a rebuild — `claude plugin marketplace update glean-indexing-sdk`, then `claude plugin update glean-connector-builder@glean-indexing-sdk`. ## What the agent does Eight skills coordinate the build. You confirm scope and review the result; the agent does the reading, wiring, and iterating. 1 Describe the sourceYour code Plain language is enough: what system, what content, and who should be able to see it. 2 Explore the APISDK Reads the source's documentation to establish endpoints, auth, pagination, and where permissions live. 3 Confirm the planYour code Scope, connector type, and permission model come back for your approval before any code is written. 4 GenerateSDK Writes the data client and connector against this SDK, including pagination, rate limiting, and ACL mapping. 5 TestSDK Runs the mocked phase, then the real-source phase against recorded fixtures — before anything is uploaded to Glean. 6 Review, then indexYour code Check the review items below, then run a full crawl and confirm documents are searchable. ### The eight skills `connector-builder`Top-level workflow coordinating the rest. `connector-api-exploration`Reading and confirming the source's API documentation. `connector-auth`Authentication patterns for source systems. `connector-pull`Data clients, pagination, rate limiting. `connector-push`Uploading documents and identities. `connector-testing`The three-phase testing workflow. `connector-observability`Logging and metrics wiring. `connector-deployment`Generating and applying deployment artifacts. They encode the same guidance as these docs — notably crawl semantics, the rule most easily got wrong: a full crawl must cover the entire confirmed scope before completing, because stale-document deletion removes anything absent from the run. The sources live in [`skills/`](https://github.com/gleanwork/glean-indexing-sdk/tree/main/skills) if you want to add one. ## Before you trust it, review these An agent produces a plausible connector quickly. These are the places a plausible connector is most often wrong — and the reason the rest of these docs exist. [PermissionsAre real ACLs attached, and does get\_identities() push the users and groups they reference? An allow-all default is the most consequential thing to miss.](/libraries/indexing-sdk/permissions.md)[Full-crawl completenessDoes the data client raise on a failed page rather than returning what it has? A short result set deletes documents as stale.](/libraries/indexing-sdk/push/error-handling.md)[Timestampscreated\_at and updated\_at must be epoch seconds, not ISO strings. This fails silently — documents just sort wrong.](/libraries/indexing-sdk/quickstart.md)[Rate limitingConfigured against your source's real quota, not left at the conservative default of one retry.](/libraries/indexing-sdk/pull/rate-limiting.md)[Incremental crawlsIf it claims incremental support, does it override \_get\_last\_crawl\_timestamp()? Without that, since is always None.](/libraries/indexing-sdk/concepts/indexing-modes.md)[Run the real testsGenerated code is written against the API docs. Phase 2 checks it against what the source actually returns.](/libraries/indexing-sdk/testing/integration.md) ## Two methods, five stages Whether an agent writes it or you do, a connector is the same pipeline. This is the shape of what gets generated. Source systemYour wiki, catalog, database `get_source_data()`Fetch raw records `transform()`Map to Glean documents `PushUploader`Batch, retry, upload Glean indexSearchable, permission-aware You write thisThe SDK handles thisExternal system ## What's in the box The agent wires these up as your source needs them. Each page is also the reference for doing it yourself. [Connector typesFour base classes: in-memory, sync streaming, async streaming, and people.](/libraries/indexing-sdk/concepts/connector-types.md)[Pull integrationsHTTP client with retries and backoff, three pagination modes, token-bucket rate limiting.PullHttpClient](/libraries/indexing-sdk/pull/http-client.md)[Push & indexingDocuments, users, groups, memberships, employees — batched and uploaded in parallel.PushUploader](/libraries/indexing-sdk/push/uploader.md)[PermissionsPer-document ACLs plus the identity graph that makes them evaluate.](/libraries/indexing-sdk/permissions.md)[TestingThree phases: fully mocked, real-source record and replay, and live end-to-end.TestHarness](/libraries/indexing-sdk/testing/overview.md)[ObservabilityStructured logs and per-stage timings, with optional CloudWatch and Google Cloud plugins.](/libraries/indexing-sdk/observability.md)[Status & debuggingAnswer "why is my document not in search?" without guessing.glean-idx document](/libraries/indexing-sdk/status-and-debugging.md)[DeploymentGenerate Docker and Terraform for a scheduled job on AWS or GCP.glean-idx deploy](/libraries/indexing-sdk/deployment/overview.md) ## Pick a base class The four connector types differ in how data flows through them, not in what they produce. Start with the first if you're unsure. BaseDatasourceConnector The whole dataset fits comfortably in memory. Wikis, service catalogs, config databases. Data client`BaseDataClient` Produces`DocumentDefinition` Peak memoryWhole dataset BaseStreamingDatasourceConnector The dataset is large or paginated and your source client is synchronous. Data client`BaseStreamingDataClient` Produces`DocumentDefinition` Peak memoryOne batch BaseAsyncStreamingDatasourceConnector Same as streaming, but your source client is async — httpx.AsyncClient, aiohttp. Data client`BaseAsyncStreamingDataClient` Produces`DocumentDefinition` Peak memoryOne batch BasePeopleConnector You're indexing employee and identity records rather than documents. Data client`BaseDataClient` Produces`EmployeeInfoDefinition` Peak memoryWhole dataset ## Three test phases, one added dependency at a time Each phase swaps exactly one thing from mocked to real, so a failure tells you which layer broke. [1Unit](/libraries/indexing-sdk/testing/unit.md) [SourceMocked](/libraries/indexing-sdk/testing/unit.md) [GleanMocked](/libraries/indexing-sdk/testing/unit.md) [SpeedMilliseconds](/libraries/indexing-sdk/testing/unit.md) [CredentialsNone](/libraries/indexing-sdk/testing/unit.md) [run\_connector with a static data client. No network at all.](/libraries/indexing-sdk/testing/unit.md) [Does transform() produce the documents I expect?](/libraries/indexing-sdk/testing/unit.md) [2Integration](/libraries/indexing-sdk/testing/integration.md) [SourceReal](/libraries/indexing-sdk/testing/integration.md) [GleanMocked](/libraries/indexing-sdk/testing/integration.md) [SpeedFast after first run](/libraries/indexing-sdk/testing/integration.md) [CredentialsSource only](/libraries/indexing-sdk/testing/integration.md) [Records real source responses to NDJSON, then replays them offline.](/libraries/indexing-sdk/testing/integration.md) [Does my data client parse what the source really returns?](/libraries/indexing-sdk/testing/integration.md) [3End-to-end](/libraries/indexing-sdk/testing/end-to-end.md) [SourceReal](/libraries/indexing-sdk/testing/end-to-end.md) [GleanReal](/libraries/indexing-sdk/testing/end-to-end.md) [SpeedSlow](/libraries/indexing-sdk/testing/end-to-end.md) [CredentialsSource + Glean](/libraries/indexing-sdk/testing/end-to-end.md) [No mocking. Uploads to whatever GLEAN\_SERVER\_URL points at.](/libraries/indexing-sdk/testing/end-to-end.md) [Does the whole thing work against a live instance?](/libraries/indexing-sdk/testing/end-to-end.md) ## SDK or the Indexing API directly? Both push into the same index. The difference is how much you build yourself. Indexing SDKIndexing API directly LanguagePythonAny HTTP client Batching and upload sessionsBuilt inYou implement it Retries and rate limitingBuilt inYou implement it Pagination against your sourceBuilt inYou implement it Testing without a live instanceBuilt inYou implement it Structured logging and metricsBuilt inYou implement it Deployment scaffoldingglean-idx deploy generates itYou implement it Use the **SDK** for a connector that runs on a schedule against a source system — most custom connectors. Use the **API directly** when indexing from a non-Python service, pushing occasional one-off documents, or attaching [custom metadata](/api-info/indexing/custom-metadata/overview.md) to documents already in Glean. The SDK is a client for the Indexing API, not a replacement — the [API documentation](/api-info/indexing/getting-started/overview.md) stays the reference for the wire protocol. --- # CLI The SDK ships one command, `glean-idx`. It covers the whole loop: checking your credentials, validating a plan, running a connector, inspecting what it uploaded, and deploying it on a schedule. ``` glean-idx doctor # are my credentials right? glean-idx validate ./my-connector # is the plan complete, before writing code? glean-idx test --phase mock # Glean mocked; connector source clients unchanged glean-idx test --phase integration # real source, recorded/replayed; Glean mocked glean-idx run --mode incremental # additive crawl for real glean-idx datasource status --datasource companywiki glean-idx deploy init --cloud gcp # Docker and Terraform for a CronJob ``` ## Where each command runs[​](#where-each-command-runs "Direct link to Where each command runs") Commands fall into three groups, and knowing which is which saves the most common confusion. `glean-idx --help` restates it. **Credentials only.** Most commands need nothing but `GLEAN_SERVER_URL` and `GLEAN_INDEXING_API_TOKEN`. They never import your code, so they run from any directory — including with nothing installed: ``` uvx --from glean-indexing-sdk glean-idx doctor ``` **Your connector too.** `run`, `test`, and `datasource configure` import your connector class, so they run from inside the connector project with the SDK installed alongside your code: ``` uv run glean-idx run ``` A command in the second group run from the wrong directory tells you exactly that, and lists the directories it searched for `glean_deployment.yaml`. **Deployment config plus standard tools.** `deploy` commands read `glean_deployment.yaml` and delegate to Docker Buildx, Terraform, the selected provider, or `kubectl`. Install and authenticate those tools first. The SDK owns connector-specific naming, image selection, exact secret manifests, Terraform variables, and workload discovery; it does not create or authenticate your cloud account, cluster, registry, identity foundation, or namespace. ## Commands[​](#commands "Direct link to Commands") ### Before you write code[​](#before-you-write-code "Direct link to Before you write code") | Command | What it does | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `glean-idx doctor` | Checks credentials, and with `--datasource NAME` proves the token actually works by reading that datasource. | | `glean-idx validate [DIR]` | Checks a connector's `.glean/` planning artifacts are complete and confirmed. Exits `5` and lists every problem at once. | | `glean-idx schema [NAME]` | Prints the JSON Schema for the models `transform()` returns. Run with no name to list them. | ### Running and testing[​](#running-and-testing "Direct link to Running and testing") | Command | What it does | | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `glean-idx run [--connector MODULE:CLASS_OR_FACTORY] [--mode full\|incremental]` | Fetches, transforms, and uploads. `--mode incremental` overrides the project's `indexing_mode`; `--force-restart` discards a partial replacement upload. | | `glean-idx test --phase PHASE [--connector MODULE:CLASS_OR_FACTORY]` | Runs the connector at one fidelity: `mock`, `integration`, `live`, or `all`. | `--phase` maps onto the [testing phases](/libraries/indexing-sdk/testing/overview.md): | Flag | Phase | Source | Glean | | --------------------- | ------------------------------------------------------------- | --------------------------- | ------ | | `--phase mock` | [Unit](/libraries/indexing-sdk/testing/unit.md) | the connector's own clients | mocked | | `--phase integration` | [Integration](/libraries/indexing-sdk/testing/integration.md) | real, recorded and replayed | mocked | | `--phase live` | [End-to-end](/libraries/indexing-sdk/testing/end-to-end.md) | real | real | `--phase all` runs them in order and stops at the first failure. A phase that cannot run is skipped and reported rather than failing the batch. Without Glean credentials, `live` is skipped and the earlier phases still run. If live can run, `--phase all` also requires `--allow-destructive-live`. `--max-items N` limits source consumption for every discovered data client. It is not a Glean-side document cap and does not guarantee a small blast radius. In particular, using it with a full live crawl intentionally produces a partial replacement, so documents outside that subset can become stale. Every live phase requires `--allow-destructive-live` and prompts you to confirm the resolved Glean target. For a full live crawl, the flag acknowledges that the run finalizes replacement state and may stale-delete documents it does not produce. Use a disposable datasource; `--yes` only skips the prompt, not the required acknowledgement. ``` glean-idx test --phase live --mode full --allow-destructive-live glean-idx test --phase all --allow-destructive-live ``` ### Inspecting what landed[​](#inspecting-what-landed "Direct link to Inspecting what landed") | Command | What it does | | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `glean-idx datasource status --datasource NAME` | Uploaded and indexed counts per object type, identity counts, visibility, and the most recent upload and processing runs. | | `glean-idx datasource process --datasource NAME` | Requests processing now instead of waiting for the next scheduled run. | | `glean-idx datasource configure [--connector MODULE:CLASS_OR_FACTORY]` | Validates and registers the connector's own `configuration`, so the datasource cannot drift from what the connector uploads to. API-facing datasource names may contain only ASCII letters and digits. An ambiguous transport failure is reconciled by reading state back, never by blindly repeating the write. | | `glean-idx document status --datasource NAME --document TYPE ID [--poll]` | Whether one or more documents finished indexing. Repeat `--document TYPE ID` to check several. | | `glean-idx document access --datasource NAME --object-type TYPE --id ID --user EMAIL` | Whether a given user can see a document. | | `glean-idx document events --datasource NAME --object-type TYPE --id ID` | A document's lifecycle events, for when it uploaded but never became searchable. | `document status` and `document access` are the two worth reaching for first when something is missing from search — see [Status and debugging](/libraries/indexing-sdk/status-and-debugging.md). ### Removing things[​](#removing-things "Direct link to Removing things") | Command | What it does | | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `glean-idx document delete --datasource NAME --document TYPE ID` | Removes one or more documents from the index. Repeat `--document TYPE ID`; prompts unless `--yes`. | | `glean-idx datasource teardown --datasource NAME` | Removes every document, keeping the datasource and its configuration. Requires typing the datasource name to confirm. | warning `teardown` cannot be undone. It uploads an empty full crawl, so everything currently indexed for that datasource becomes stale and is removed. ### Deploying[​](#deploying "Direct link to Deploying") `glean-idx deploy` generates and operates a scheduled job in your own cloud: | Command | What it does | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `deploy init` | Generates Docker, Terraform, runtime, and YAML artifacts. | | `deploy build [--push]` | Delegates to Docker Buildx using the YAML image tag. The compatibility `--tag` option must match the YAML so build and apply cannot diverge. | | `deploy secrets upload` | Uploads exact `.env` values and records only validated key names locally. | | `deploy apply` | Runs Terraform init, displays an exact saved plan, prompts, and applies that same plan. | | `deploy run` | Delegates to `kubectl` to create one immediate Job from the configured CronJob. | | `deploy status` / `deploy logs` | Uses `kubectl` against the currently selected cluster. | | `deploy destroy` | Removes Terraform resources and manifest-owned secrets, then reports retained image, namespace, datasource registration, and local files. | See [Deployment](/libraries/indexing-sdk/deployment/overview.md) for prerequisites, provider authentication, namespace ownership, and cleanup boundaries. ## Output and exit codes[​](#output-and-exit-codes "Direct link to Output and exit codes") Every command takes `--output json` and returns a stable envelope, which is what makes the CLI usable from a script or an agent: ``` glean-idx datasource status --datasource companywiki --output json | jq .data.documents ``` ``` { "ok": true, "data": { "datasource": "companywiki", "documents": { "uploaded": { "article": 42 } } } } ``` Output defaults to text at a terminal and JSON when redirected, so a piped command is machine-readable without passing anything. In JSON mode the envelope goes to **stdout whether the command succeeded or not** — an `ok: false` result is still the result — so there is one stream to parse. Text-mode errors go to stderr, as does connector logging from `run`. SDK-owned exit codes are stable. When a deployment subprocess starts and exits nonzero, the CLI preserves that Docker, Terraform, or `kubectl` exit code (from `1` through `255`) and includes the command and captured diagnostics in the error envelope. | Code | Meaning | | ---- | --------------------------------------------------------------------------- | | `0` | Succeeded. | | `1` | Unexpected failure, or the connector itself raised. | | `2` | Bad invocation. | | `3` | Environment not ready: no credentials, no project, no importable connector. | | `4` | Glean rejected the request or was unreachable. | | `5` | The command ran and found its subject invalid. | Failures carry a machine-readable `code`, and where one exists, the command that fixes them: ``` { "ok": false, "error": { "code": "missing_credentials", "message": "missing required environment variables: GLEAN_SERVER_URL, GLEAN_INDEXING_API_TOKEN", "hint": ["export GLEAN_SERVER_URL=...", "export GLEAN_INDEXING_API_TOKEN=..."] } } ``` ## Unattended use[​](#unattended-use "Direct link to Unattended use") `--yes` skips every confirmation, which destructive commands otherwise require. `datasource teardown` asks you to type the datasource name rather than pressing `y`, since the effect is unrecoverable and covers the whole datasource. ## Shell completion[​](#shell-completion "Direct link to Shell completion") ``` glean-idx completion zsh >> ~/.zshrc # or bash, fish ``` The script goes to stdout and the install hint to stderr, so redirecting into a startup file captures only what belongs there. --- # Architecture A connector is a pipeline. You own the two stages specific to your source; the SDK owns batching, retries, upload sessions, and stale-document cleanup. Source systemYour wiki, catalog, database `get_source_data()`Fetch raw records `transform()`Map to Glean documents `PushUploader`Batch, retry, upload Glean indexSearchable, permission-aware You write thisThe SDK handles thisExternal system ## The indexing lifecycle[​](#the-indexing-lifecycle "Direct link to The indexing lifecycle") `BaseDatasourceConnector.index_data()` runs the sequence below. Each stage is timed separately, so a slow connector can be attributed to fetch, transform, or upload without adding instrumentation. 1 Start observabilitySDK Begins execution timing and assigns a `run_id` for the run. 2 Identity crawl — `get_identities()`Your code Users are bulk-indexed first. If groups are returned, memberships must be too — otherwise the SDK raises `InconsistentDataError`, because groups without memberships produce ACLs that can never match anyone. 3 Resolve `since`Your code On an incremental crawl, calls `_get_last_crawl_timestamp()`. The base implementation returns `None`. 4 Content crawl — `get_data()`Your code Delegates to your data client’s `get_source_data()`. 5 Transform — `transform()`Your code Your mapping from source records to Glean entity definitions. 6 UploadSDK A full crawl calls `PushUploader.bulk_index_documents()` for replacement and stale cleanup. An incremental crawl calls additive `index_documents()`. 7 End observabilitySDK Records totals. On exception, increments the error counter and re-raises so a scheduler sees a non-zero exit. Sync and async streaming datasource connectors keep fetch/transform memory bounded, but their indexing implementations do **not** run the optional `get_identities()` crawl. Push identities separately before a streaming content crawl. Their full uploads still use replacement semantics, while incremental batches use additive uploads. ## Class hierarchy[​](#class-hierarchy "Direct link to Class hierarchy") Connectors and data clients pair by shape: a streaming connector expects a streaming data client. ``` BaseConnector[TSourceData, TIndexableEntityDefinition] ├── BaseDatasourceConnector[T] → DocumentDefinition ├── BaseStreamingDatasourceConnector[T] → DocumentDefinition (sync generator) ├── BaseAsyncStreamingDatasourceConnector[T] → DocumentDefinition (async generator) └── BasePeopleConnector[T] → EmployeeInfoDefinition BaseDataClient[T] → Sequence[T] ├── BaseStreamingDataClient[T] → Generator[T] ├── BaseAsyncStreamingDataClient[T] → AsyncGenerator[T] └── BasePullHttpStreamingDataClient[T] (HTTP + pagination, from the pull recipes) ``` See [Connector types](/libraries/indexing-sdk/concepts/connector-types.md) for which pair to use. ## Errors[​](#errors "Direct link to Errors") Every SDK exception derives from `GleanError` and carries a `fix_suggestion` and, where applicable, a `docs_url`. Both are included in `str(error)`, so an unhandled failure tells the operator what to do next. ``` GleanError ├── GleanConfigurationError (also a ValueError) │ ├── MissingEnvironmentVariableError │ └── InvalidDatasourceConfigError └── GleanValidationError (also a ValueError) ├── InvalidPropertyError ├── InconsistentDataError └── UnsupportedConnectorTypeError ``` Both branches subclass `ValueError`, so existing `except ValueError` handlers keep working. See [Error handling](/libraries/indexing-sdk/push/error-handling.md) for what to catch and — more importantly — what never to swallow. ## Configuration[​](#configuration "Direct link to Configuration") Two environment variables. There is no configuration file. | Variable | Purpose | | -------------------------- | --------------------------------------------------------- | | `GLEAN_SERVER_URL` | Your Glean backend URL, e.g. `https://acme-be.glean.com`. | | `GLEAN_INDEXING_API_TOKEN` | Datasource-scoped Indexing API token. | | `GLEAN_INSTANCE` | Deprecated fallback for `GLEAN_SERVER_URL`. | A missing variable raises `MissingEnvironmentVariableError` at client construction, not mid-upload. --- # Connector types The SDK ships four connector base classes. They differ in how data flows through them, not in what they produce. BaseDatasourceConnector The whole dataset fits comfortably in memory. Wikis, service catalogs, config databases. Data client`BaseDataClient` Produces`DocumentDefinition` Peak memoryWhole dataset BaseStreamingDatasourceConnector The dataset is large or paginated and your source client is synchronous. Data client`BaseStreamingDataClient` Produces`DocumentDefinition` Peak memoryOne batch BaseAsyncStreamingDatasourceConnector Same as streaming, but your source client is async — httpx.AsyncClient, aiohttp. Data client`BaseAsyncStreamingDataClient` Produces`DocumentDefinition` Peak memoryOne batch BasePeopleConnector You're indexing employee and identity records rather than documents. Data client`BaseDataClient` Produces`EmployeeInfoDefinition` Peak memoryWhole dataset If you're unsure, start with `BaseDatasourceConnector`. Switching to a streaming variant later means changing the base class and the return type of `get_source_data()` — `transform()` is unchanged. ## In memory[​](#in-memory "Direct link to In memory") `get_source_data()` returns a `Sequence`. The connector fetches everything, transforms everything, then uploads in batches of `batch_size` (default 1000). ``` class WikiDataClient(BaseDataClient[WikiPage]): def get_source_data(self, since=None, **kwargs): return fetch_all_pages() class WikiConnector(BaseDatasourceConnector[WikiPage]): configuration = CustomDatasourceConfig(name="wiki", display_name="Wiki") def transform(self, data): return [to_document(page) for page in data] ``` Peak memory holds every source record **and** every transformed document at once. At a few hundred thousand documents that becomes the binding constraint — and the fix is a streaming connector, not a smaller batch size, because batching happens after transform. ## Sync streaming[​](#sync-streaming "Direct link to Sync streaming") `get_source_data()` is a generator. The connector pulls `batch_size` items at a time, transforms that slice, and uploads it before pulling more. ``` from collections.abc import Generator from glean.indexing.connectors import ( BaseStreamingDataClient, BaseStreamingDatasourceConnector, ) class ArticleDataClient(BaseStreamingDataClient[Article]): def get_source_data(self, **kwargs) -> Generator[Article, None, None]: page = 1 while True: batch = fetch_page(page) if not batch: return yield from batch page += 1 class ArticleConnector(BaseStreamingDatasourceConnector[Article]): configuration = CustomDatasourceConfig(name="articles", display_name="Articles") def transform(self, data): return [to_document(article) for article in data] ``` Note that `transform()` still receives a `Sequence` — the SDK slices the generator into batches for you. You never write batching logic. Full-mode batches form one replacement upload; incremental-mode batches use additive `index_documents()` calls. tip If your source is a paginated HTTP API, use [`BasePullHttpStreamingDataClient`](/libraries/indexing-sdk/pull/data-clients.md) instead of hand-rolling the loop above. It handles pagination, retries, and rate limiting. ## Async streaming[​](#async-streaming "Direct link to Async streaming") Same model, async generators. Drive it with `index_data_async()`. ``` from collections.abc import AsyncGenerator from glean.indexing.connectors import ( BaseAsyncStreamingDataClient, BaseAsyncStreamingDatasourceConnector, ) class EventDataClient(BaseAsyncStreamingDataClient[Event]): async def get_source_data(self, **kwargs) -> AsyncGenerator[Event, None]: async for event in stream_events(): yield event class EventConnector(BaseAsyncStreamingDatasourceConnector[Event]): configuration = CustomDatasourceConfig(name="events", display_name="Events") def transform(self, data): return [to_document(event) for event in data] await connector.index_data_async(mode=IndexingMode.FULL) ``` `transform()` stays synchronous. Only fetching is async. Push identities separately The sync and async streaming connector implementations do not call the optional `get_identities()` crawl inherited from `BaseDatasourceConnector`. If streamed documents reference datasource users or groups, upload those identities before the streaming content run. info Async connectors also expose the synchronous `index_data()`, which wraps the async path in `asyncio.run()`. That works from a plain script but fails inside an existing event loop. In tests, use `await run_connector_async(...)`; `run_connector()` raises a clear error if you call it on an async connector from inside a running loop. ## People[​](#people "Direct link to People") `BasePeopleConnector` produces `EmployeeInfoDefinition` instead of `DocumentDefinition`. Configuration, `batch_size`, and observability behave the same. ``` class EmployeeConnector(BasePeopleConnector[EmployeeRecord]): configuration = CustomDatasourceConfig(name="hris", display_name="HRIS") def transform(self, data): return [to_employee(record) for record in data] ``` Employee data feeds Glean's people directory and knowledge graph. It's distinct from the **datasource identities** (users, groups, memberships) that back document ACLs — see [Permissions](/libraries/indexing-sdk/permissions.md). ## Batch size[​](#batch-size "Direct link to Batch size") All four classes expose `batch_size`, defaulting to 1000. ``` connector = WikiConnector(name="wiki", data_client=client) connector.batch_size = 250 ``` Lower it if your documents are large enough to hit upload timeouts. For timeout and concurrency tuning, see [Batching and throughput](/libraries/indexing-sdk/push/batching.md). --- # Indexing modes ``` from glean.indexing.models import IndexingMode connector.index_data(mode=IndexingMode.FULL) connector.index_data(mode=IndexingMode.INCREMENTAL) ``` | | `IndexingMode.FULL` | `IndexingMode.INCREMENTAL` | | --------------------------- | ------------------- | ---------------------------------- | | Scope fetched | Everything in scope | Only records changed since `since` | | Deletes stale documents | Yes | No | | Propagates source deletions | Yes | No | | Cost per run | High | Low | | Safe to run partially | **Never** | Yes | ## Full crawls[​](#full-crawls "Direct link to Full crawls") A full crawl is a **complete replacement of the indexed state**. Every document currently in scope is fetched and indexed, and previously indexed documents absent from the run are deleted as stale. That last part is the important one. Stale-document deletion is what makes deletions at the source propagate to Glean — and it's also what makes a *partial* full crawl dangerous. danger Never let a partial or failed fetch finish as a successful full crawl. If your source API returns 12 of 10,000 pages because of a transient error and the connector treats that as complete, Glean deletes the other 9,988 documents as stale. Raise on incomplete fetches rather than returning short. A failed crawl is recoverable. A successful crawl that deleted 90% of your index is not. Pagination and streaming bound memory, not scope. A streaming connector still has to cover the entire confirmed scope before the run is allowed to complete. Use a full crawl for the initial load, after changing `transform()`, and on a periodic cadence to reconcile drift. ## Incremental crawls[​](#incremental-crawls "Direct link to Incremental crawls") The connector-building skills generate full crawls only If you are building a connector with the SDK's skills, they will not write incremental logic for you, and that is deliberate — `connector-builder` records incremental crawl as developer-owned follow-up after a full crawl works end to end, and `connector-pull` will not implement it unless you ask. Incremental is harder to validate: it needs a durable checkpoint *and* a reliable source-side deletion signal before it is correct rather than merely faster. Whether it is available at all depends on the source. A modified-since filter is usually offered per object type, so one endpoint may support it while another in the same API does not, and a connector can end up incremental for some objects and full for the rest. Everything below is the developer-owned path. An incremental crawl passes a `since` timestamp down to your data client so you can query only what changed: ``` class WikiDataClient(BaseDataClient[WikiPage]): def get_source_data(self, since=None, **kwargs): if since: return fetch_pages_modified_after(since) return fetch_all_pages() ``` Incremental crawls upload documents additively with `index_documents()`: they add or update records and do **not** delete documents omitted from that run. An empty incremental result is a no-op. Only a full crawl reconciles deletions, which is why most connectors run incrementally on a short cadence and fully on a longer one. A full crawl always finalizes replacement state, including when the source is empty. A successful empty full crawl sends one complete empty bulk page so all previously indexed documents are reconciled as stale. ### The SDK does not persist checkpoints[​](#the-sdk-does-not-persist-checkpoints "Direct link to The SDK does not persist checkpoints") This is the part that surprises people. `IndexingMode.INCREMENTAL` calls `_get_last_crawl_timestamp()` on your connector, and the base implementation **returns `None`** — which means `since` is `None` and your data client falls back to a full fetch. To get real incremental behavior, override it and supply the timestamp from wherever you store it: ``` class WikiConnector(BaseDatasourceConnector[WikiPage]): def _get_last_crawl_timestamp(self): # your storage: file, S3, DynamoDB, database return read_checkpoint("companywiki") def index_data(self, mode=IndexingMode.FULL, options=None): started_at = datetime.now(timezone.utc).isoformat() super().index_data(mode=mode, options=options) write_checkpoint("companywiki", started_at) # only on success ``` Record the timestamp from *before* the crawl started, and only write it after the run succeeds. Writing the end time risks missing records modified while the crawl was in flight; writing on failure silently skips a window. ## Connector options[​](#connector-options "Direct link to Connector options") `ConnectorOptions` adjusts upload behavior for a single run. ``` from glean.indexing.models import ConnectorOptions connector.index_data( mode=IndexingMode.FULL, options=ConnectorOptions(force_restart=True), ) ``` | Option | Default | Effect | | ------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------- | | `force_restart` | `False` | Discards any in-progress upload session and starts a new one. Use after a crashed run leaves an upload stuck — not as a default. | | `disable_stale_deletion_check` | `False` | Forces synchronous stale-document deletion after the upload completes. | | `upload_timeout_ms` | `None` | Per-call timeout for bulk upload requests only. Raise it for large batches. | | `upload_max_workers` | `5` | Concurrent middle-page uploads. First and last pages are always sequential. | | `document_batch_size_bytes` | `5 MiB` | Maximum serialized bytes per document batch. Set `None` to batch by document count only. | Datasource and streaming connectors forward `document_batch_size_bytes` to the document batch processor. A connector can override `_resolve_max_batch_bytes()` when the byte cap must be selected dynamically; that connector-level result takes precedence over the option. Direct `PushUploader` calls expose the same limit as `max_batch_bytes`. ## Choosing a schedule[​](#choosing-a-schedule "Direct link to Choosing a schedule") A common pattern: * **Incremental every 15–60 minutes** — keeps search fresh at low cost. * **Full nightly or weekly** — reconciles deletions and repairs drift. Match the incremental cadence to how precise your source's modified-at filter is. If it has minute granularity, overlap the window slightly rather than risking a gap; re-indexing an unchanged document is a no-op from the reader's point of view. --- # Deployment overview A connector is a batch job: it starts, pulls from a source, pushes to Glean, and exits. `glean-idx deploy` generates and operates a Docker image and Terraform for a Kubernetes CronJob on GKE or EKS. `glean-idx` is the connector-specific interface. It delegates image operations to Docker, infrastructure operations to Terraform, and workload inspection to `kubectl`. You install and authenticate those standard tools, and your platform team continues to own the cloud project or account, cluster, registry, identity foundation, and Kubernetes namespace. ## Before you begin[​](#before-you-begin "Direct link to Before you begin") Install the cloud extra for the provider you use: ``` pip install "glean-indexing-sdk[gcp]" # GKE, Artifact Registry, Secret Manager # or pip install "glean-indexing-sdk[aws]" # EKS, ECR, Secrets Manager ``` All deployments require: * Docker with a running daemon and Buildx. * Terraform 1.0 or later. * `kubectl`, authenticated to the target cluster. GKE users also need the `gke-gcloud-auth-plugin` executable available on `PATH`. * A connector project with `glean-indexing-sdk` installed. * `GLEAN_SERVER_URL`, `GLEAN_INDEXING_API_TOKEN`, and every source credential your connector reads. * An existing container registry repository and Kubernetes namespace. * Permission to push images, create the generated workload identity and IAM bindings, manage connector secrets, and create Kubernetes resources in the namespace. The generated Terraform does **not** create a project/account, cluster, registry, Workload Identity/IRSA foundation, or namespace. It reads the namespace before planning, so a missing or inaccessible namespace fails before resources are mutated. Use a dedicated namespace when your organization permits it; never let connector teardown own a shared namespace. ### GCP prerequisites[​](#gcp-prerequisites "Direct link to GCP prerequisites") GCP requires an existing GKE cluster and Artifact Registry repository. Enable the GKE, Artifact Registry, Secret Manager, IAM, Cloud Logging, and Cloud Monitoring APIs. Enable Workload Identity on the cluster and its node pools. Set these shell variables to your environment before running the checks: `PROJECT_ID`, `GKE_LOCATION`, `CLUSTER_NAME`, `ARTIFACT_REGISTRY_LOCATION`, `ARTIFACT_REGISTRY_REPOSITORY`, and `NAMESPACE`. ``` docker info docker buildx version terraform version gcloud --version gke-gcloud-auth-plugin --version kubectl version --client gcloud auth login --update-adc gcloud config set project "$PROJECT_ID" gcloud auth configure-docker "${ARTIFACT_REGISTRY_LOCATION}-docker.pkg.dev" gcloud container clusters get-credentials "$CLUSTER_NAME" \ --location "$GKE_LOCATION" \ --project "$PROJECT_ID" ``` Confirm that the customer-managed resources exist and that `kubectl` points to the intended cluster: ``` gcloud container clusters describe "$CLUSTER_NAME" \ --location "$GKE_LOCATION" \ --project "$PROJECT_ID" gcloud artifacts repositories describe "$ARTIFACT_REGISTRY_REPOSITORY" \ --location "$ARTIFACT_REGISTRY_LOCATION" \ --project "$PROJECT_ID" kubectl cluster-info kubectl get namespace "$NAMESPACE" || kubectl create namespace "$NAMESPACE" ``` `region` in `glean_deployment.yaml` is the GKE **location** accepted by `gcloud ... --location`; it may be a region such as `us-central1` or a zone such as `us-central1-a`. For a private-only cluster with a GKE DNS control-plane endpoint, set `cluster_endpoint` to its bare `*.gke.goog` hostname—no `https://`, port, path, or trailing slash. The generated Kubernetes provider uses system trust for an explicit DNS endpoint and the cluster CA for the auto-discovered IP endpoint. The machine running Terraform must still have network access to the selected endpoint. ### AWS prerequisites[​](#aws-prerequisites "Direct link to AWS prerequisites") AWS requires an existing EKS cluster, ECR repository, Kubernetes namespace, and IAM OIDC provider for IRSA. Authenticate the AWS CLI and Docker, then select the cluster explicitly: ``` aws sts get-caller-identity aws eks update-kubeconfig --name "$CLUSTER_NAME" --region "$AWS_REGION" kubectl cluster-info kubectl get namespace "$NAMESPACE" || kubectl create namespace "$NAMESPACE" aws ecr get-login-password --region "$AWS_REGION" | docker login --username AWS --password-stdin \ "$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com" ``` ## Generate and configure the deployment[​](#generate-and-configure-the-deployment "Direct link to Generate and configure the deployment") Run `init` from the connector project: ``` glean-idx deploy init --cloud gcp \ --connector-class CompanyWikiConnector \ --connector-factory create_connector ``` `--connector-name` is a cloud resource slug. It may contain lowercase letters, digits, hyphens, and underscores. It is separate from `CustomDatasourceConfig.name`, which is the Glean datasource identifier and may contain only ASCII letters and digits. For example, deployment slug `company-wiki` can run datasource `companywiki`. ### Generated files[​](#generated-files "Direct link to Generated files") | File | Purpose | | ------------------------ | ---------------------------------------------------------------------------------------------------- | | `glean_deployment.yaml` | Authoritative deployment configuration; edit this file before each operation. | | `Dockerfile` | Non-root connector image. | | `.dockerignore` | Excludes credentials, Terraform state, virtualenvs, tests, caches, coverage, and local build output. | | `run.py` | Cloud entrypoint that loads exact declared secrets and calls the connector factory. | | `terraform/main.tf` | CronJob, workload identity, exact secret access, logging, and metrics resources. | | `terraform/variables.tf` | Apply-time inputs populated from `glean_deployment.yaml`. | | `.env.example` | Template for Glean and source credentials. | Generated Terraform is an artifact, not the configuration interface. Do not hand-edit it. Change `glean_deployment.yaml` instead; `deploy apply` passes every mutable setting at plan time. ### Configuration reference[​](#configuration-reference "Direct link to Configuration reference") | Key | Default | Notes | | -------------------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `connector_name` | sanitized current directory name | Cloud resource name and secret prefix; not necessarily the Glean datasource name. | | `connector_class` / `connector_module` | `MyConnector` / `connector` | Connector import target. | | `connector_factory` | unset | Optional zero-argument factory; preferred when construction needs source credentials. | | `cloud` | required | `gcp` or `aws`; fixed when `deploy init` generates provider-specific Terraform. | | `region` | `us-central1` / `us-east-1` | GCP cluster location or AWS region. | | `cluster_name` | placeholder | Existing GKE or EKS cluster. | | `namespace` | `default` | Existing, customer-managed Kubernetes namespace. | | `image_tag` | `latest` | Image tag used by both `deploy build` and `deploy apply`. Use an immutable release identifier for controlled rollouts. | | `cpu` | `500m` | Pod CPU request and limit. | | `memory` | `512Mi` | Pod memory request and limit. | | `cron_schedule` | `0 2 * * *` | UTC cron expression. | | `indexing_mode` | `FULL` | `FULL` or `INCREMENTAL`. | | `project_id`, `artifact_registry_repo`, `service_account_name` | provider-specific | GCP project, repository URL, and optional workload service-account name. | | `cluster_endpoint` | unset | Optional bare GKE DNS endpoint for a private-only cluster. | | `account_id`, `ecr_repo`, `iam_role_name` | provider-specific | AWS account, ECR repository URI, and optional IRSA role name. | To change cloud providers, rerun `deploy init --cloud ...` into a reviewed output directory; editing `cloud` alone cannot convert the generated provider files. A full and incremental schedule need separate deployment configurations because a Kubernetes CronJob has one schedule and indexing mode. They may point to the same immutable image. See [Indexing modes](/libraries/indexing-sdk/concepts/indexing-modes.md). ## Deploy[​](#deploy "Direct link to Deploy") The canonical sequence is: 1 Validate locally Run the mock and integration phases before any cloud mutation. 2 Register the datasource `glean-idx datasource configure` validates the alphanumeric datasource name and submits its object definitions. 3 Build and push `glean-idx deploy build --push` delegates to Docker Buildx using the configured image tag. 4 Upload exact secrets `glean-idx deploy secrets upload` writes values to the provider and records only validated key names in `.glean_secret_keys`. 5 Plan and apply `glean-idx deploy apply` runs Terraform init, displays an exact saved plan, prompts, and applies that same plan. 6 Run and verify `glean-idx deploy run` creates one Job immediately; status, logs, and document polling verify the result. ``` glean-idx deploy init --cloud gcp \ --connector-name company-wiki \ --connector-class CompanyWikiConnector \ --connector-module connector \ --connector-factory create_connector cp .env.example .env # Edit glean_deployment.yaml and fill in .env before continuing. glean-idx datasource configure --connector connector:CompanyWikiConnector glean-idx deploy build --push glean-idx deploy secrets upload glean-idx deploy apply ``` Build before uploading secrets. `.env` and `.glean_secret_keys` are excluded from the image. `deploy apply` reads the current YAML and current secret manifest; post-`init` edits to schedule, mode, resources, import metadata, namespace, identity names, endpoint, and image tag are reflected in the displayed plan. If datasource configuration loses its connection after sending the request, the SDK reads the configuration back without issuing a second write. Equivalent state counts as success. Divergent or unreadable state is reported as an ambiguous outcome; inspect it before retrying. ## Run now and verify[​](#run-now-and-verify "Direct link to Run now and verify") A newly applied CronJob may not be scheduled for hours. Start one Job immediately through the same deployment interface: ``` glean-idx deploy run glean-idx deploy status glean-idx deploy logs --follow glean-idx document status \ --datasource companywiki \ --document article page_123 \ --poll ``` `deploy run` delegates to `kubectl create job --from=cronjob/...`, derives the CronJob and namespace from the YAML, and returns the exact Job name. A completed Kubernetes Job proves only that the process exited successfully; document status proves that Glean uploaded and indexed the expected content. ## Secrets[​](#secrets "Direct link to Secrets") The generated workload can access only the secret names recorded by the latest successful `secrets upload`. GCP receives one secret-level IAM member per key; AWS resolves and grants the exact secret ARNs. The workload cannot enumerate secrets. Missing declared secrets fail startup. `GLEAN_SERVER_URL` and `GLEAN_INDEXING_API_TOKEN` are always required. Add every source credential read by your factory—for example `WIKI_API_TOKEN`—to `.env` as well. Never commit `.env`, copy it into the image, put secret values in YAML, or pass them as Terraform variables. ## Teardown and retained resources[​](#teardown-and-retained-resources "Direct link to Teardown and retained resources") Content and cloud infrastructure have separate lifecycles: ``` glean-idx datasource teardown --datasource companywiki glean-idx deploy destroy ``` * `datasource teardown` submits an empty full replacement. It removes indexed documents after asynchronous processing but retains the datasource registration and configuration. * `deploy destroy` removes Terraform-managed workload/IAM resources and, unless `--keep-secrets` is used, exact manifest-owned secrets. * `deploy destroy` does **not** delete the container image, customer-owned namespace, datasource registration, or local generated files. Its text and JSON output list those retained artifacts. Remove retained resources only when they are dedicated to this connector: ``` # GCP: delete the configured image/tag or apply your registry lifecycle policy. gcloud artifacts docker images delete "$IMAGE_REFERENCE" --delete-tags # AWS: delete the dedicated tag or apply your ECR lifecycle policy. aws ecr batch-delete-image \ --repository-name "$ECR_REPOSITORY_NAME" \ --image-ids "imageTag=$IMAGE_TAG" \ --region "$AWS_REGION" # Customer-owned namespace: delete only when it contains no shared workloads. kubectl delete namespace "$NAMESPACE" ``` There is no Indexing API operation to delete a datasource registration. Use the Glean admin surface when complete registration removal is required. ## Running elsewhere[​](#running-elsewhere "Direct link to Running elsewhere") The Python connector does not require Kubernetes. It can run as a VM cron job, Cloud Run or Lambda job, Airflow task, or scheduled CI workload. It needs Glean credentials **plus every source-specific runtime variable used by the connector**. The portable execution primitive is the connector factory invoked by `glean-idx run`; generated `run.py` files are provider-specific implementations for the GKE/EKS deployment path. --- # Observability Every connector base class creates a `ConnectorObservability` instance and wires it into the indexing lifecycle. You get stage timings, item counts, and structured logs without adding any instrumentation. ``` connector.index_data(mode=IndexingMode.FULL) print(connector.observability.get_metrics_summary()) ``` ## What you get for free[​](#what-you-get-for-free "Direct link to What you get for free") `index_data()` times each stage separately and records counts: | Metric | Meaning | | ----------------------- | ------------------------------------ | | `data_fetch` | Time in your data client. | | `data_transform` | Time in `transform()`. | | `data_upload` | Time uploading to Glean. | | `items_fetched` | Records returned by the source. | | `documents_transformed` | Documents produced by `transform()`. | | `documents_indexed` | Documents handed to the uploader. | | `indexing_errors` | Incremented when the run raises. | Separate stage timings are the main diagnostic tool for a slow connector — see [Batching and throughput](/libraries/indexing-sdk/push/batching.md). The uploader and the pull HTTP client add more when wired up: upload batch sizes, throughput, API request latency and counts, retries, and crawl success/failure. ## Structured logging[​](#structured-logging "Direct link to Structured logging") ``` from glean.indexing.observability import setup_connector_logging setup_connector_logging("companywiki", log_level="INFO") ``` Structured JSON logging is on by default, which is what makes logs queryable once shipped to an aggregator — "show me every event for document `page_123`" needs fields, not prose. | Argument | Default | Purpose | | ------------------------ | -------- | ---------------------------------------------------- | | `connector_name` | — | Identifies the connector in every record. | | `log_level` | `"INFO"` | Standard level name. | | `log_format` | `None` | Custom format string; overrides structured logging. | | `use_structured_logging` | `True` | Emit JSON. | | `formatter` | `None` | Custom `logging.Formatter`; overrides the two above. | | `extra_handlers` | `None` | Additional handlers. | | `logger_provider` | `None` | A cloud logging provider. | `StructuredFormatter` and `CompactStructuredFormatter` are available if you want to attach them elsewhere. ## Metrics providers[​](#metrics-providers "Direct link to Metrics providers") `MetricsProvider` is the extension point. The default is `NoOpMetricsProvider` — metrics are recorded in-process for `get_metrics_summary()` but not exported. ``` from glean.indexing.observability import ConnectorObservability, InMemoryMetricsProvider observability = ConnectorObservability( connector_name="companywiki", datasource="companywiki", crawl_mode="full", metrics_provider=InMemoryMetricsProvider(), ) ``` | Provider | Use | | --------------------------- | -------------------------------------------------------------------------------- | | `NoOpMetricsProvider` | Default. No export. | | `InMemoryMetricsProvider` | Local development and tests. Exposes `get_metrics()` and `get_metric_history()`. | | `CloudWatchMetricsProvider` | AWS. Requires the `aws` extra. | | `CloudMonitoringProvider` | GCP. Requires the `gcp` extra. | Each observability instance gets a `run_id` (a UUID unless you pass one), which is how you correlate every log line and metric from a single crawl. `InMemoryMetricsProvider` synchronizes concurrent metric updates, so exact counter assertions are safe during parallel uploads. ## AWS[​](#aws "Direct link to AWS") ``` pip install "glean-indexing-sdk[aws]" ``` ``` from glean.indexing.observability import ConnectorObservability, setup_connector_logging from glean.indexing.observability.plugins.aws import ( CloudWatchLogsProvider, CloudWatchMetricsProvider, ) setup_connector_logging( "companywiki", logger_provider=CloudWatchLogsProvider( log_group="/glean/connectors", log_stream="companywiki", region_name="us-east-1", ), ) observability = ConnectorObservability( connector_name="companywiki", metrics_provider=CloudWatchMetricsProvider( namespace="GleanConnectors", region_name="us-east-1", dimensions={"connector": "companywiki"}, ), ) ``` The logging provider is named `CloudWatchLogsProvider` and requires both `log_group` and `log_stream`; there is no `CloudWatchLoggerProvider`. ## GCP[​](#gcp "Direct link to GCP") ``` pip install "glean-indexing-sdk[gcp]" ``` ``` from glean.indexing.observability.plugins.gcp import ( CloudLoggingProvider, CloudMonitoringProvider, ) ``` info The plugin packages import lazily. If the extra isn't installed, importing the plugin module emits a `UserWarning` naming the missing extra rather than raising — so a misconfigured deployment degrades to no telemetry instead of crashing. Check for that warning if metrics stop appearing. warning The cloud provider plugins are currently tested only against mocks, never against real CloudWatch or Cloud Monitoring APIs. Validate them in a staging environment before relying on them for production alerting. Tracked in [issue #108](https://github.com/gleanwork/glean-indexing-sdk/issues/108). ## Custom providers[​](#custom-providers "Direct link to Custom providers") Subclass `MetricsProvider` and implement `emit_metric()` and `flush()`: ``` from glean.indexing.observability import MetricsProvider, MetricType class StatsdMetricsProvider(MetricsProvider): def emit_metric(self, name, value, metric_type=MetricType.GAUGE, labels=None): ... def flush(self) -> None: ... ``` `record_counter()`, `record_gauge()`, and `record_histogram()` are provided on the base class in terms of `emit_metric()`. ## Manual instrumentation[​](#manual-instrumentation "Direct link to Manual instrumentation") ``` obs = connector.observability obs.start_timer("enrichment") enrich(documents) obs.end_timer("enrichment") obs.record_metric("enriched_documents", len(documents)) obs.increment_counter("enrichment_failures") ``` `PerformanceTracker` (context manager), `@with_observability` (class decorator for automatic method logging), and `@track_crawl_progress` are also available. info Custom fields passed to structured logging cannot collide with reserved `LogRecord` attribute names — `ConnectorObservability` raises `ValueError` listing the conflicts rather than producing corrupted records. ## What to alert on[​](#what-to-alert-on "Direct link to What to alert on") * `indexing_errors` above zero, or a non-zero process exit. * A crawl that hasn't succeeded within its expected window. * `documents_indexed` dropping sharply run over run — the signal for a partial crawl that's about to delete documents as stale. That last one is the alert worth having. See [Indexing modes](/libraries/indexing-sdk/concepts/indexing-modes.md). --- # Permissions Glean enforces permissions at query time. A document is only returned to a user who is allowed to see it — which means your connector has to tell Glean two things, and both are required. Source ACLsWho can see what, at the source Per-document ACLsDocumentPermissionsDefinition `get_identities()`Users, groups, memberships Enforced at query timeResults scoped per user You write thisThe SDK handles thisExternal system An ACL naming a group Glean has never heard of matches nobody. danger Never index sensitive content with an allow-all ACL "for now." Permissions are the boundary between a useful connector and a data leak, and retrofitting them means re-indexing everything. Get them right in the first run. ## Per-document ACLs[​](#per-document-acls "Direct link to Per-document ACLs") Attach a `DocumentPermissionsDefinition` to each document: ``` from glean.api_client.models import ( DocumentDefinition, DocumentPermissionsDefinition, PermissionsGroupIntersectionDefinition, UserReferenceDefinition, ) def transform(self, data): return [ DocumentDefinition( id=article["id"], title=article["title"], datasource=self.name, view_url=article["url"], permissions=DocumentPermissionsDefinition( allowed_groups=article["visible_to_groups"], allowed_users=[ UserReferenceDefinition(email=email) for email in article["visible_to_users"] ], allowed_group_intersections=[ PermissionsGroupIntersectionDefinition( required_groups=["engineering", "employees"] ) ], ), ) for article in data ] ``` | Field | Meaning | | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `allowed_users` | Users who can see the document, by email or datasource user ID. | | `allowed_groups` | Group names that can see the document. | | `allowed_group_intersections` | A list of `PermissionsGroupIntersectionDefinition(required_groups=[...])` entries. A user must belong to every group within one entry; multiple entries are alternative intersections. | Map your source's model onto these directly. Don't flatten groups into user lists: group membership changes constantly, and a flattened ACL is stale the moment someone joins a team. ## Datasource identities[​](#datasource-identities "Direct link to Datasource identities") Implement `get_identities()` to push the identity graph: ``` from glean.indexing.models import DatasourceIdentityDefinitions def get_identities(self) -> DatasourceIdentityDefinitions: return DatasourceIdentityDefinitions( users=fetch_users(), groups=fetch_groups(), memberships=fetch_memberships(), ) ``` `BaseDatasourceConnector.index_data()` runs this **before** its content crawl, so identities exist by the time documents referencing them arrive. The sync and async streaming connector implementations do not call `get_identities()`; push their users, groups, and memberships separately before indexing documents, or use a non-streaming datasource connector when you need the built-in identity crawl. info If you return `groups`, you must also return `memberships`. The SDK raises `InconsistentDataError` otherwise, because a group with no members produces ACLs that can never match. Returning only `users` is valid when your source has no group concept. ## Email-based references[​](#email-based-references "Direct link to Email-based references") Setting `is_user_referenced_by_email=True` on the datasource config lets you reference users by email, which is usually simplest when your source and Glean share an identity provider: ``` configuration = CustomDatasourceConfig( name="companywiki", display_name="Company Wiki", is_user_referenced_by_email=True, ) ``` Without it, references use datasource-specific user IDs, and you must push a user record mapping each ID. ## Verifying enforcement[​](#verifying-enforcement "Direct link to Verifying enforcement") Counting indexed documents proves nothing about permissions. Test the negative case: a user who should *not* see a document must not see it. The test harness supports this directly. List identities that must not have access to any transformed document: testing\_config.yaml ``` testing: negative_test_identities: - contractor@example.com - external-partners ``` Phase 2 checks literal references in `allowed_users`, `allowed_groups`, and each intersection's `required_groups`. It also rejects missing `permissions`, `allow_anonymous_access=True`, and `allow_all_datasource_users_access=True`, because any of those prevents the harness from proving a configured identity is denied. See [Integration testing](/libraries/indexing-sdk/testing/integration.md). You can also assert in code: ``` from glean.indexing.testing import assert_negative_identities_absent, extract_permission_refs result = run_connector(connector) refs = extract_permission_refs(result.documents_posted) assert "engineering" in refs.group_ids assert_negative_identities_absent(result.documents_posted, ["contractor@example.com"]) ``` `extract_permission_refs()` walks `allowed_users`, `allowed_groups`, and `allowed_group_intersections`, returning the deduplicated `user_ids` and `group_ids` your documents actually reference. It's also useful for indexing *only* the identities your crawl needs, rather than the whole directory. Finally, confirm end to end against a real instance and search as a restricted user. If they can see a document they shouldn't, the ACL or the identity graph is wrong — the search result is the ground truth, not the upload response. ## Related[​](#related "Direct link to Related") ### [Indexing API permissions](/api-info/indexing/documents/permissions.md) [The underlying permissions model and field reference.](/api-info/indexing/documents/permissions.md) ### [Testing](/libraries/indexing-sdk/testing/overview.md) [Verify ACLs before you ship.](/libraries/indexing-sdk/testing/overview.md) --- # Data clients A data client is the source-facing half of a connector. At minimum it implements `get_source_data()`: ``` from glean.indexing.connectors import BaseDataClient class WikiDataClient(BaseDataClient[WikiPage]): def get_source_data(self, since=None, **kwargs): return fetch_all_pages() ``` That's fine for small sources. For a paginated HTTP API, the SDK gives you a base class that already knows how to page. ## BasePullHttpStreamingDataClient[​](#basepullhttpstreamingdataclient "Direct link to BasePullHttpStreamingDataClient") `BasePullHttpStreamingDataClient` combines [`PullHttpClient`](/libraries/indexing-sdk/pull/http-client.md) with [pagination](/libraries/indexing-sdk/pull/pagination.md) and yields records one at a time. For a well-behaved JSON API you often need no method body at all: ``` from glean.indexing.recipes.pull import BasePullHttpStreamingDataClient, PullOptions class ArticleDataClient(BasePullHttpStreamingDataClient[Article]): def __init__(self, token: str): super().__init__( base_url="https://api.example.com/v2", path="/articles", items_key="items", pagination="link", page_size=100, headers={"Authorization": f"Bearer {token}"}, options=PullOptions(timeout_seconds=30.0), ) ``` It subclasses `BaseStreamingDataClient`, so it belongs with `BaseStreamingDatasourceConnector` — see [Connector types](/libraries/indexing-sdk/concepts/connector-types.md). ## Constructor reference[​](#constructor-reference "Direct link to Constructor reference") | Argument | Default | Purpose | | ------------------------------ | ---------------------------- | ----------------------------------------------------------------------- | | `base_url` | — | Base URL for relative paths. | | `path` | — | Endpoint path to fetch. | | `items_key` | `"items"` | Key holding the record array. Set to `None` if the body *is* the array. | | `pagination` | `"link"` | One of `"link"`, `"offset"`, `"cursor"`, `"none"`. | | `params` | `None` | Static query parameters sent on every request. | | `page_size` | `None` | Page size. Required and must be positive for offset pagination. | | `max_items` | `None` | Stop after N records. Invaluable for local development. | | `offset_param` / `limit_param` | `"offset"` / `"limit"` | Parameter names for offset pagination. | | `start_offset` | `0` | Starting offset. | | `cursor_param` / `cursor_key` | `"cursor"` / `"next_cursor"` | Request parameter and response key for cursor pagination. | | `initial_cursor` | `None` | Cursor for the first request. | | `headers` | `None` | Default headers. | | `options` | `PullOptions()` | Timeout, retry, and redaction behavior. | | `rate_limiter` | `None` | See [Rate limiting](/libraries/indexing-sdk/pull/rate-limiting.md). | | `observability` | `None` | Emits fetch-started and fetch-completed events. | | `client` | `None` | Bring your own `httpx.Client`. | | `timeout_seconds` | `None` | Per-request override. | Invalid combinations fail at construction, not mid-crawl: offset pagination without a positive `page_size` raises `ValueError`, as does a negative `max_items`. ## Extracting records[​](#extracting-records "Direct link to Extracting records") `items_key` controls where records are read from. ``` # {"items": [...]} → items_key="items" (default) # [...] → items_key=None # {"data": {"records": []}} → override get_source_data ``` If the value at `items_key` isn't a list, the client raises `TypeError` naming the key and the type it actually found — a fast failure when a source changes its envelope. ## Customizing[​](#customizing "Direct link to Customizing") Everything above is a starting point. Override `get_source_data()` when your source needs something the base class doesn't model, and reuse `self.http`: ``` class ArticleDataClient(BasePullHttpStreamingDataClient[Article]): def get_source_data(self, **kwargs): for article in super().get_source_data(**kwargs): detail = self.http.get(f"/articles/{article['id']}") yield {**article, "body": detail.json_dict()["body"]} ``` That pattern — list endpoint for discovery, detail endpoint for content — is common, and the N+1 request cost is exactly why you want a [rate limiter](/libraries/indexing-sdk/pull/rate-limiting.md) configured. ## Cleaning up[​](#cleaning-up "Direct link to Cleaning up") The client owns an HTTP connection. Close it when the crawl finishes: ``` try: connector.index_data(mode=IndexingMode.FULL) finally: data_client.close() ``` ## Limiting work during development[​](#limiting-work-during-development "Direct link to Limiting work during development") `max_items` caps the number of records fetched, which keeps iteration fast while you're still shaping `transform()`: ``` ArticleDataClient(token, max_items=25) ``` The [test harness](/libraries/indexing-sdk/testing/integration.md) applies its own per-client `max_items` from `TestConfig`, so you don't have to hardcode this for tests. --- # HTTP client `PullHttpClient` is the SDK's HTTP client for talking to **your source system**. It is not the client that talks to Glean — that's [`PushUploader`](/libraries/indexing-sdk/push/uploader.md). It exists because every hand-rolled connector ends up reimplementing the same things: session reuse, retry with backoff, honoring `Retry-After`, and keeping API keys out of logs. ``` from glean.indexing.recipes.pull import PullHttpClient, PullOptions with PullHttpClient( base_url="https://api.example.com/v2", headers={"Authorization": f"Bearer {token}"}, options=PullOptions(timeout_seconds=30.0), ) as http: response = http.get("/articles", params={"limit": 100}) articles = response.json_dict()["items"] ``` ## Requests[​](#requests "Direct link to Requests") | Method | Returns | | -------------------------------------------------------------------- | ------------------------------------------- | | `get(path_or_url, *, params, headers, timeout_seconds)` | `PullResponse` | | `post(path_or_url, *, json, data, params, headers, timeout_seconds)` | `PullResponse` | | `request(method, path_or_url, ...)` | `PullResponse` | | `get_bytes(path_or_url, *, headers, timeout_seconds, max_bytes)` | `tuple[bytes, str]` — body and content type | Paths are resolved against `base_url`; absolute URLs are used as-is, which is what makes [`Link`-header pagination](/libraries/indexing-sdk/pull/pagination.md) work without extra bookkeeping. ## Responses[​](#responses "Direct link to Responses") `PullResponse` is a frozen dataclass with two accessors that fail loudly on shape mismatches: ``` response.status_code # int response.headers # dict[str, str] response.url # str response.json_dict() # dict — raises TypeError if the body was a list response.json_list() # list — raises TypeError if the body was an object ``` Preferring these over raw `.data` means a source that starts returning `{"error": ...}` where you expected a list fails with a clear `TypeError` at the boundary instead of an `AttributeError` deep inside `transform()`. ## Retries and backoff[​](#retries-and-backoff "Direct link to Retries and backoff") Retry behavior lives on `PullRetryOptions`: ``` options = PullOptions( timeout_seconds=30.0, retries=PullRetryOptions( max_attempts=5, initial_backoff_seconds=1.0, max_backoff_seconds=60.0, backoff_multiplier=2.0, retry_status_codes={429, 500, 502, 503, 504}, retry_connection_errors=True, respect_retry_after=True, jitter_seconds=1.0, ), ) ``` | Field | Default | Notes | | ------------------------- | --------------------------- | --------------------------------------------------------------------------------- | | `max_attempts` | `2` | Total attempts, not retries after the first. | | `initial_backoff_seconds` | `1.0` | First wait, then multiplied each attempt. | | `max_backoff_seconds` | `60.0` | Ceiling on any single wait. | | `backoff_multiplier` | `2.0` | Exponential factor. | | `retry_status_codes` | `{429, 500, 502, 503, 504}` | Everything else fails immediately. | | `retry_connection_errors` | `True` | Retries transport-level failures. | | `respect_retry_after` | `True` | A `Retry-After` header overrides computed backoff. | | `jitter_seconds` | `1.0` | Random jitter added to each wait, so parallel connectors don't retry in lockstep. | info The default `max_attempts=2` is deliberately conservative — one retry. Most production connectors should raise it to 4 or 5, particularly against APIs that rate-limit aggressively. Pair it with a [rate limiter](/libraries/indexing-sdk/pull/rate-limiting.md) so you're pacing requests rather than only reacting to 429s. Failures that exhaust retries raise `PullHttpError`, which carries `status_code` and the underlying `response`: ``` from glean.indexing.recipes.pull import PullHttpError try: response = http.get("/articles") except PullHttpError as error: if error.status_code == 404: return [] raise ``` ## Credential redaction[​](#credential-redaction "Direct link to Credential redaction") `PullOptions.mask_params` defaults to `True`, so query parameters are redacted in the client's request logs. This matters because source APIs frequently accept tokens as query parameters, and connector logs are routinely shipped to a log aggregator. Redaction covers logging only. It does not stop you from putting a secret in an exception message you construct yourself. ## Bounded binary fetches[​](#bounded-binary-fetches "Direct link to Bounded binary fetches") `get_bytes()` takes a `max_bytes` cap, useful when indexing attachments from a source that can return arbitrarily large files: ``` content, content_type = http.get_bytes("/attachments/42", max_bytes=10 * 1024 * 1024) ``` warning `get_bytes()` currently reads the full response into memory before applying `max_bytes`, so the cap bounds what you *receive*, not what gets buffered. Don't rely on it as protection against a source returning a multi-gigabyte file. Tracked in [issue #119](https://github.com/gleanwork/glean-indexing-sdk/issues/119). ## Connection lifecycle[​](#connection-lifecycle "Direct link to Connection lifecycle") The client owns an `httpx.Client` unless you pass your own. Use it as a context manager, or call `close()` explicitly: ``` http = PullHttpClient(base_url="https://api.example.com") try: ... finally: http.close() ``` If you pass `client=my_httpx_client`, the SDK will not close it — ownership stays with you. ## Next[​](#next "Direct link to Next") ### [Data clients](/libraries/indexing-sdk/pull/data-clients.md) [Wire this into a streaming data client with no boilerplate.](/libraries/indexing-sdk/pull/data-clients.md) ### [Pagination](/libraries/indexing-sdk/pull/pagination.md) [Link header, offset, and cursor strategies.](/libraries/indexing-sdk/pull/pagination.md) --- # Pagination `BasePullHttpStreamingDataClient` supports four pagination modes. Set `pagination=` and the matching parameters; the client walks pages and yields records until the source runs out or `max_items` is reached. | Mode | Use when the API… | | ---------- | ---------------------------------------------------- | | `"link"` | Returns an RFC 5988 `Link` header with `rel="next"`. | | `"offset"` | Takes numeric `offset` and `limit` parameters. | | `"cursor"` | Returns an opaque cursor token in the response body. | | `"none"` | Returns everything in one response. | ## Link header[​](#link-header "Direct link to Link header") The default. GitHub, GitLab, and most APIs that follow RFC 5988 use this. ``` super().__init__( base_url="https://api.example.com/v2", path="/articles", pagination="link", page_size=100, ) ``` The client reads `Link` (case-insensitively), finds the `rel="next"` URL, and follows it as an absolute URL. Query parameters from the first request are not re-applied, because the `next` URL already carries them. The parser tolerates the messy variants seen in the wild: commas inside URLs, `rel=next` unquoted, and multi-valued rels like `rel="next prev"`. ## Offset[​](#offset "Direct link to Offset") ``` super().__init__( base_url="https://api.example.com/v2", path="/articles", pagination="offset", page_size=100, # required, must be > 0 offset_param="offset", limit_param="limit", start_offset=0, ) ``` Each request sends `?limit=100&offset=N`, incrementing by `page_size`. Paging stops when a page comes back empty. warning Offset pagination is unstable against a source being written to during the crawl. Inserting a record on page 1 shifts everything, so a record can be skipped or seen twice. If the source offers cursor or link pagination, prefer it. ## Cursor[​](#cursor "Direct link to Cursor") ``` super().__init__( base_url="https://api.example.com/v2", path="/articles", pagination="cursor", cursor_param="cursor", # request parameter name cursor_key="next_cursor", # response body key page_size=100, ) ``` After each page, the client reads `cursor_key` from the response body and sends it as `cursor_param` on the next request. Paging stops when the cursor is missing, empty, not a string, or **identical to the previous cursor**. That last check is a deliberate guard against a source that echoes the same token forever, which would otherwise spin indefinitely. ## None[​](#none "Direct link to None") ``` super().__init__(base_url="...", path="/config", pagination="none", items_key=None) ``` One request, all records. ## Limiting total records[​](#limiting-total-records "Direct link to Limiting total records") `max_items` applies across pages in every mode: ``` super().__init__(..., pagination="link", page_size=100, max_items=250) ``` The client trims the final page so exactly 250 records are yielded, then stops requesting. `max_items=0` yields nothing without issuing a request. ## Non-standard pagination[​](#non-standard-pagination "Direct link to Non-standard pagination") If your source does something else — a page number, a timestamp watermark, a `has_more` boolean — override `get_source_data()` and drive `self.http` yourself: ``` class ArticleDataClient(BasePullHttpStreamingDataClient[Article]): def get_source_data(self, **kwargs): page = 1 while True: response = self.http.get(self.path, params={"page": page, "per_page": 100}) body = response.json_dict() yield from body["items"] if not body.get("has_more"): return page += 1 ``` You keep retries, backoff, rate limiting, and redaction from `PullHttpClient`; you replace only the paging loop. danger Whatever loop you write must terminate on a source that misbehaves. Bound it — by page count, by a repeated-token check, or by `max_items` — so a stuck API can't turn into an infinite crawl. On a full crawl, a loop that exits early also deletes documents as stale. --- # Rate limiting Connectors are the most aggressive client most source APIs will ever see: a full crawl issues thousands of requests as fast as the network allows. Rate limiting keeps you inside the source's quota, and keeps your connector from being the reason someone else's integration gets throttled. ## Token bucket[​](#token-bucket "Direct link to Token bucket") ``` from glean.indexing.recipes.pull import ( BasePullHttpStreamingDataClient, TokenBucketRateLimiter, ) class ArticleDataClient(BasePullHttpStreamingDataClient[Article]): def __init__(self, token: str): super().__init__( base_url="https://api.example.com/v2", path="/articles", headers={"Authorization": f"Bearer {token}"}, rate_limiter=TokenBucketRateLimiter( rate_per_second=10, capacity=20, ), ) ``` Tokens refill continuously at `rate_per_second`, up to `capacity`. Every request consumes one token, blocking if none are available. | Parameter | Meaning | | ----------------- | ---------------------------------------------------------------------------- | | `rate_per_second` | Sustained request rate. Must be greater than zero. | | `capacity` | Burst size — how many requests can go out back-to-back after an idle period. | | `initial_tokens` | Starting tokens. Defaults to full capacity. | Setting `capacity` above `rate_per_second` allows a short burst, which is usually what you want: the crawl starts fast, then settles to the sustained rate. The limiter is thread-safe, so it can be shared across data clients that hit the same API: ``` shared = TokenBucketRateLimiter(rate_per_second=10, capacity=20) articles = ArticleDataClient(token, rate_limiter=shared) comments = CommentDataClient(token, rate_limiter=shared) ``` That's the right pattern when a source enforces its quota per token rather than per endpoint. ## Choosing a rate[​](#choosing-a-rate "Direct link to Choosing a rate") Start from the source's documented quota and leave headroom — other things use the same token. A source allowing 600 requests/minute is 10/second; running at 7–8 leaves room for retries and for whatever else is authenticating as you. ## Timeouts[​](#timeouts "Direct link to Timeouts") `acquire()` blocks indefinitely by default. To fail instead of waiting forever, set a rate-limit timeout: ``` from glean.indexing.recipes.pull import PullOptions options = PullOptions(rate_limit_timeout_seconds=30.0) ``` If capacity doesn't arrive in time, the SDK raises `RateLimitExceededError`. Waiting forever is the safer default for a scheduled batch job; a timeout is better when the connector runs behind something with its own deadline. ## Rate limiting and retries work together[​](#rate-limiting-and-retries-work-together "Direct link to Rate limiting and retries work together") They solve different problems and both should be on: * The **rate limiter** is proactive — it paces requests to stay under the quota. * **Retries** are reactive — when a 429 gets through anyway, `PullRetryOptions` backs off and tries again, honoring `Retry-After` by default. ``` super().__init__( base_url="https://api.example.com/v2", path="/articles", rate_limiter=TokenBucketRateLimiter(rate_per_second=10, capacity=20), options=PullOptions( retries=PullRetryOptions(max_attempts=5, respect_retry_after=True), ), ) ``` A rising retry count is a signal your configured rate is too high. [Observability](/libraries/indexing-sdk/observability.md) records retries, so you can see it rather than guess. ## Custom limiters[​](#custom-limiters "Direct link to Custom limiters") `RateLimiter` is a `Protocol`. Anything with a matching `acquire()` works — a distributed limiter backed by Redis, for instance, when several connector instances share one quota: ``` class RedisRateLimiter: def acquire(self, tokens: float = 1.0, timeout_seconds: float | None = None) -> None: ... # raise RateLimitExceededError if capacity doesn't arrive in time ``` info `TokenBucketRateLimiter` accumulates minor floating-point drift over very long continuous runs. It has no practical effect on a crawl measured in hours. Tracked in [issue #121](https://github.com/gleanwork/glean-indexing-sdk/issues/121). --- # Batching and throughput The defaults work for most connectors. Reach for these knobs when a crawl is timing out, running too slowly, or overwhelming your source. | Knob | Where | Default | Reach for it when… | | --------------------------- | ----------------------- | -------------- | ----------------------------------------------------------------------------------------- | | `connector.batch_size` | Connector attribute | `1000` | Batches time out — **lower** it. | | `document_batch_size_bytes` | `ConnectorOptions` | `5 MiB` | Tune the serialized byte cap for connector-managed uploads; `None` disables the byte cap. | | `max_batch_bytes` | `PushUploader` argument | `5 MiB` | The equivalent cap for direct uploader calls. | | `upload_timeout_ms` | `ConnectorOptions` | client default | Large batches hit timeouts. | | `upload_max_workers` | `ConnectorOptions` | `5` | Many small batches over a high-latency link. | ## Find the bottleneck first[​](#find-the-bottleneck-first "Direct link to Find the bottleneck first") Before tuning anything, look at where time is going. The SDK times fetch, transform, and upload separately: ``` connector.index_data(mode=IndexingMode.FULL) print(connector.observability.get_metrics_summary()) ``` | Dominant stage | Likely fix | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `data_fetch` | Source API is slow or rate-limited. Tune [rate limiting](/libraries/indexing-sdk/pull/rate-limiting.md), reduce N+1 detail calls, or use a streaming connector to overlap work. | | `data_transform` | Expensive work in `transform()` — HTML parsing, regex, per-document network calls. Move network calls into the data client. | | `data_upload` | Tune the knobs below. | Tuning upload concurrency when 90% of wall-clock is `data_fetch` accomplishes nothing. See [Observability](/libraries/indexing-sdk/observability.md). ## Batch size[​](#batch-size "Direct link to Batch size") Batches close on whichever limit hits first: `batch_size` documents or `max_batch_bytes` serialized bytes. ``` connector = WikiConnector(name="wiki", data_client=client) connector.batch_size = 250 ``` Set the byte cap for a connector-managed run with the exact `ConnectorOptions` field: ``` connector.index_data( options=ConnectorOptions(document_batch_size_bytes=2 * 1024 * 1024) ) ``` The connector forwards that value to byte-aware batching for in-memory, sync streaming, and async streaming uploads. Custom connectors can override the static `_resolve_max_batch_bytes(options)` hook when the cap must be selected at runtime; the hook's result is the effective cap. Lower `batch_size` when your documents are large. A connector indexing full page bodies will hit the 5 MiB byte cap long before 1000 documents, so the count limit is doing nothing and you're paying serialization cost to discover that each time. Setting `batch_size` closer to what actually fits makes batching cheaper and upload sizes more predictable. Raising `batch_size` above 1000 is rarely useful — the byte cap almost always binds first. ## Timeouts[​](#timeouts "Direct link to Timeouts") A batch that exceeds the request timeout fails and retries the whole batch, which is expensive. If you see timeouts, do one of: ``` # Give large batches longer. connector.index_data(options=ConnectorOptions(upload_timeout_ms=120_000)) # Or make the batches smaller. connector.batch_size = 250 ``` Smaller batches are usually the better answer. They fail cheaper, retry faster, and give more granular progress in logs. ## Concurrency[​](#concurrency "Direct link to Concurrency") `upload_max_workers` controls how many middle pages upload at once: ``` connector.index_data(options=ConnectorOptions(upload_max_workers=10)) ``` The first and last pages are always sequential — the first opens the upload session, the last closes it and triggers stale-document deletion. Concurrency helps when you're latency-bound: many small batches, each spending most of its time waiting on the network. It doesn't help when you're bandwidth-bound or when the source is the bottleneck. Set it to `1` to serialize uploads while debugging. ## Memory[​](#memory "Direct link to Memory") For in-memory connectors, peak memory holds every source record and every transformed document simultaneously. If a full crawl gets OOM-killed, the fix isn't a smaller batch size — batching happens after transform. Switch to a [streaming connector](/libraries/indexing-sdk/concepts/connector-types.md), which transforms and uploads one batch at a time and keeps memory bounded regardless of total document count. --- # Error handling Every SDK exception derives from `GleanError` and carries a `fix_suggestion` — and often a `docs_url` — that are included in `str(error)`. An unhandled failure prints what to do about it, not just what went wrong. ``` from glean.indexing.exceptions import GleanError try: connector.index_data(mode=IndexingMode.FULL) except GleanError as error: print(error) # message + "How to fix:" + "Documentation:" print(error.fix_suggestion) print(error.docs_url) ``` ## Hierarchy[​](#hierarchy "Direct link to Hierarchy") ``` GleanError ├── GleanConfigurationError (also a ValueError) │ ├── MissingEnvironmentVariableError │ └── InvalidDatasourceConfigError └── GleanValidationError (also a ValueError) ├── InvalidPropertyError ├── InconsistentDataError └── UnsupportedConnectorTypeError ``` Configuration errors and validation errors both subclass `ValueError`, so existing `except ValueError` handlers keep working. | Exception | Raised when | | --------------------------------- | --------------------------------------------------------------------------------------------------------- | | `MissingEnvironmentVariableError` | `GLEAN_SERVER_URL` or `GLEAN_INDEXING_API_TOKEN` is unset. Raised at client construction, not mid-upload. | | `InvalidDatasourceConfigError` | `CustomDatasourceConfig` is missing `name` or `display_name`. | | `InvalidPropertyError` | A property definition has an invalid value. | | `InconsistentDataError` | `get_identities()` returned groups without memberships. | | `UnsupportedConnectorTypeError` | A connector type isn't supported for the requested operation. | ## Source-side errors[​](#source-side-errors "Direct link to Source-side errors") Failures pulling from your source raise different types, because they're not Glean's problem: | Exception | From | | ------------------------ | --------------------------------------------------------------------------------------- | | `PullHttpError` | A source request failed after exhausting retries. Carries `status_code` and `response`. | | `RateLimitExceededError` | Rate-limit capacity didn't arrive before `rate_limit_timeout_seconds`. | ``` from glean.indexing.recipes.pull import PullHttpError try: response = self.http.get(f"/articles/{article_id}") except PullHttpError as error: if error.status_code == 404: return None # deleted between listing and fetch — skip it raise # anything else aborts the crawl ``` ## What to swallow, and what not to[​](#what-to-swallow-and-what-not-to "Direct link to What to swallow, and what not to") This is the decision that matters most in a connector, because getting it wrong deletes data. **Safe to swallow:** a single document that 404s between listing and fetch, or one record whose payload is malformed. Skip it, log it, continue. **Never swallow:** anything that makes the result set incomplete — a failed page of results, an auth expiry mid-crawl, a source-side 500 on a list endpoint. ``` def get_source_data(self, **kwargs): for summary in self.list_articles(): # raises on failure — correct try: yield self.fetch_detail(summary["id"]) except PullHttpError as error: if error.status_code == 404: logger.warning("Article %s vanished mid-crawl", summary["id"]) continue raise ``` danger On a **full crawl**, a short result set means stale-document deletion removes everything that didn't appear. Catching an exception around your paging loop and returning what you have so far converts a transient source outage into mass deletion in Glean. Let it raise. A failed crawl is recoverable; a successful crawl that deleted 90% of your index is not. ## Partial upload failures[​](#partial-upload-failures "Direct link to Partial upload failures") The SDK retries transient upload failures via the generated client's retry configuration. If an upload session fails partway, the next run picks up cleanly — but a session left in a stuck state can block subsequent uploads. Clear it with: ``` connector.index_data( mode=IndexingMode.FULL, options=ConnectorOptions(force_restart=True), ) ``` Use `force_restart` deliberately, not as a default. It discards in-progress upload state. ## Observing failures[​](#observing-failures "Direct link to Observing failures") `index_data()` increments an error counter and re-raises, so failures are visible in metrics as well as logs: ``` try: connector.index_data(mode=IndexingMode.FULL) except Exception: logger.exception("Crawl failed") raise # let the scheduler see a non-zero exit ``` Exiting non-zero matters: a scheduler that thinks the run succeeded won't alert, and won't retry. See [Observability](/libraries/indexing-sdk/observability.md). --- # PushUploader `PushUploader` is the SDK's client for the Glean Indexing API. Connector base classes use it internally, so most connectors never touch it directly. You reach for it when you need to push data outside the `fetch → transform → upload` lifecycle: event-driven updates, one-off backfills, or targeted deletes. ``` from glean.indexing.push import PushUploader uploader = PushUploader(datasource="companywiki") uploader.bulk_index_documents(documents) ``` Credentials come from `GLEAN_SERVER_URL` and `GLEAN_INDEXING_API_TOKEN`, same as everywhere else in the SDK. ## Constructor[​](#constructor "Direct link to Constructor") | Argument | Default | Purpose | | -------------------- | ------- | ------------------------------------------------------------- | | `datasource` | — | Datasource name sent with every call. | | `retries` | `None` | Generated-client retry configuration. | | `server_url` | `None` | Per-call server URL override. | | `timeout_ms` | `None` | Per-call timeout override. | | `http_headers` | `None` | Extra HTTP headers. | | `observability` | `None` | A `ConnectorObservability` to record upload logs and metrics. | | `upload_max_workers` | `5` | Concurrent middle-page uploads. Must be greater than zero. | ## Documents[​](#documents "Direct link to Documents") ### Bulk replace[​](#bulk-replace "Direct link to Bulk replace") `bulk_index_documents()` replaces the datasource's documents. This is what a full crawl runs, and it is what deletes stale documents. ``` uploader.bulk_index_documents( documents, batch_size=1000, max_batch_bytes=5 * 1024 * 1024, ) ``` | Argument | Default | Purpose | | --------------------------------------- | --------- | ---------------------------------------------------------------------- | | `upload_id` | generated | Groups batches into one upload session. | | `batch_size` | `1000` | Maximum documents per batch. | | `max_batch_bytes` | `5 MiB` | Maximum serialized bytes per batch. Set `None` to batch by count only. | | `force_restart_upload` | `None` | Discards a previous incomplete session. | | `disable_stale_document_deletion_check` | `None` | Forces synchronous stale deletion. | Batching applies both limits: a batch closes when it hits `batch_size` documents **or** `max_batch_bytes` serialized bytes, whichever comes first. That keeps one unusually large document from pushing a batch past the API's payload limit. An empty input still sends one page marked as both first and last, completing an empty replacement so previously indexed documents are reconciled as stale. danger `bulk_index_documents()` is a **replacement**. Documents absent from the call are deleted as stale. Never call it with a partial result set — see [Indexing modes](/libraries/indexing-sdk/concepts/indexing-modes.md). ### Incremental updates[​](#incremental-updates "Direct link to Incremental updates") To add or update documents without touching anything else, use the additive `index_documents()`. Incremental connector runs use this method (once per streamed batch), and an empty incremental run makes no document request: ``` uploader.index_documents(documents) ``` This is the right call for event-driven connectors reacting to a webhook. ### Deleting[​](#deleting "Direct link to Deleting") ``` uploader.delete_document(object_type="article", document_id="page_123") ``` ### Pre-supplied batches[​](#pre-supplied-batches "Direct link to Pre-supplied batches") If you're already producing batches — a streaming connector, or your own chunking — hand them over directly: ``` uploader.bulk_index_document_batches(batches, batch_count=len(batches)) ``` Pass `batch_count` when you know it. The uploader uses it to mark the final page, which is what triggers stale-document cleanup. ## Identities[​](#identities "Direct link to Identities") Document ACLs only evaluate if Glean knows about the users and groups they reference. See [Permissions](/libraries/indexing-sdk/permissions.md). ``` uploader.bulk_index_users(users=users, batch_size=1000) uploader.bulk_index_groups(groups=groups, batch_size=1000) uploader.bulk_index_memberships(memberships=memberships, batch_size=1000) ``` Single-item and delete variants exist too: `index_user()`, `index_group()`, `index_membership()`, `delete_user()`, `delete_group()`, `delete_membership()`. info Push groups and memberships together. A group with no memberships produces ACLs that can never match anyone. The connector base classes raise `InconsistentDataError` when `get_identities()` returns groups without memberships. ## Employees[​](#employees "Direct link to Employees") For people data, separate from document ACLs: ``` uploader.bulk_index_employees(employees=employees, batch_size=1000) ``` ## Parallelism[​](#parallelism "Direct link to Parallelism") `bulk_index_document_batches()` uploads middle pages concurrently via a thread pool sized by `upload_max_workers` (default 5). The **first and last pages are always sequential** — the first opens the upload session, the last closes it and triggers stale deletion, so neither can race. ``` uploader = PushUploader(datasource="companywiki", upload_max_workers=10) ``` Raise it for many small batches over a high-latency link; lower it to `1` to make an upload fully sequential when debugging. ## Parameter naming[​](#parameter-naming "Direct link to Parameter naming") Document methods take `disable_stale_document_deletion_check`; user, group, and employee bulk methods take `disable_stale_data_deletion_check`. Membership bulk uploads do not expose a stale-deletion option; they accept `force_restart_upload` and optional `group` scoping. --- # Quickstart A connector is two classes: a **data client** that fetches records from your source, and a **connector** that turns them into Glean documents. This page builds both and indexes a document you can find in search. Source systemYour wiki, catalog, database `get_source_data()`Fetch raw records `transform()`Map to Glean documents `PushUploader`Batch, retry, upload Glean indexSearchable, permission-aware You write thisThe SDK handles thisExternal system ## Install[​](#install "Direct link to Install") ``` pip install glean-indexing-sdk ``` Optional cloud observability plugins: ``` pip install "glean-indexing-sdk[aws]" # CloudWatch logs + metrics pip install "glean-indexing-sdk[gcp]" # Cloud Logging + Cloud Monitoring ``` ## Set your credentials[​](#set-your-credentials "Direct link to Set your credentials") The SDK reads these from the environment. Never hardcode them. ``` export GLEAN_SERVER_URL="https://your-company-be.glean.com" export GLEAN_INDEXING_API_TOKEN="your-indexing-api-token" export WIKI_BASE_URL="https://wiki.company.com" export WIKI_API_TOKEN="your-source-api-token" ``` info `GLEAN_INSTANCE` is still accepted as a deprecated fallback, but new connectors should use `GLEAN_SERVER_URL`. A missing variable raises `MissingEnvironmentVariableError` when the client is constructed, not part-way through an upload. ## Prefer a ready-made dataset?[​](#prefer-a-ready-made-dataset "Direct link to Prefer a ready-made dataset?") The rest of this page has you write a small data client over invented data, which is the fastest way to see the shape of a connector. If you would rather index something with a real permission model already in it — 29 documents, six groups, a few deliberately restricted files, and one restricted to named users rather than a group — there is a complete runnable example you can copy instead: ``` npx tiged --mode=git gleanwork/glean-cookbook/examples/sample-catalog sample-catalog cd sample-catalog && cat README.md ``` It registers as a [test datasource](/api-info/indexing/datasource/test-datasource.md), so ranking signals are off and nothing is visible until you allow-list yourself — which makes it safe to run against an instance other people search. Come back here for the concepts; that example is just content to point them at. ## Write a data client[​](#write-a-data-client "Direct link to Write a data client") A data client implements one method, `get_source_data()`. The `since` argument is populated on [incremental crawls](/libraries/indexing-sdk/concepts/indexing-modes.md). ``` from typing import Any, Optional, Sequence, TypedDict from glean.indexing.connectors import BaseDataClient class WikiPage(TypedDict): id: str title: str content: str author: str updated_at: str url: str class WikiDataClient(BaseDataClient[WikiPage]): def __init__(self, base_url: str, api_token: str): self.base_url = base_url self.api_token = api_token def get_source_data(self, since: Optional[str] = None, **kwargs: Any) -> Sequence[WikiPage]: # Replace with a real API call against your source. return [ { "id": "page_123", "title": "Engineering Onboarding Guide", "content": "Welcome to the engineering team...", "author": "jane.smith@company.com", "updated_at": "2026-02-01T14:30:00Z", "url": f"{self.base_url}/pages/123", } ] ``` ## Write a connector[​](#write-a-connector "Direct link to Write a connector") The connector declares its datasource configuration and maps source records to `DocumentDefinition` objects. `CustomDatasourceConfig.name` may contain only ASCII letters and digits; Glean normalizes it internally. This API-facing name is separate from cloud deployment slugs, which may contain hyphens or underscores. ``` import os from datetime import datetime from typing import List from glean.indexing.connectors import BaseDatasourceConnector from glean.indexing.models import ( ContentDefinition, CustomDatasourceConfig, DocumentDefinition, UserReferenceDefinition, ) class CompanyWikiConnector(BaseDatasourceConnector[WikiPage]): configuration = CustomDatasourceConfig( name="companywiki", display_name="Company Wiki", url_regex=r"https://wiki\.company\.com/.*", is_user_referenced_by_email=True, ) def transform(self, data: Sequence[WikiPage]) -> List[DocumentDefinition]: return [ DocumentDefinition( id=page["id"], title=page["title"], datasource=self.name, view_url=page["url"], body=ContentDefinition(mime_type="text/plain", text_content=page["content"]), author=UserReferenceDefinition(email=page["author"]), updated_at=int( datetime.fromisoformat(page["updated_at"].replace("Z", "+00:00")).timestamp() ), ) for page in data ] def create_connector() -> CompanyWikiConnector: """Construct the connector from runtime configuration.""" return CompanyWikiConnector( name="companywiki", data_client=WikiDataClient( base_url=os.environ["WIKI_BASE_URL"], api_token=os.environ["WIKI_API_TOKEN"], ), ) ``` Save the data client, connector, and factory in `connector.py`. The zero-argument `create_connector()` factory gives the CLI and generated deployment entrypoint one construction path without hardcoding credentials. warning `created_at` and `updated_at` are **integers** — Unix epoch seconds. Passing an ISO 8601 string is the most common first-connector mistake, and it surfaces later as documents sorting or displaying with the wrong date rather than as an upload error. ## Test it before you push[​](#test-it-before-you-push "Direct link to Test it before you push") Start with a static data client and a recording Glean mock. This check uses no network and needs no credentials: ``` from glean.indexing.testing import StaticDataClient, run_connector result = run_connector(CompanyWikiConnector("companywiki", StaticDataClient([ { "id": "page_123", "title": "Engineering Onboarding Guide", "content": "Welcome...", "author": "jane.smith@company.com", "updated_at": "2026-02-01T14:30:00Z", "url": "https://wiki.company.com/pages/123", } ]))) result.assert_documents_posted(count=1, datasource="companywiki") ``` Then move through the CLI phases one at a time. Both `mock` and `integration` keep Glean mocked, but they call the connector's real source client; integration also records and replays source responses. ``` glean-idx test --phase mock --connector connector:create_connector --max-items 5 glean-idx test --phase integration --connector connector:create_connector --max-items 5 ``` `max_items` limits how many source items each registered client consumes. It is not a Glean-side upload cap or a blast-radius guarantee. See [Testing](/libraries/indexing-sdk/testing/overview.md) for the full three-phase workflow. ## Register and index[​](#register-and-index "Direct link to Register and index") Register the datasource after the mocked phases pass. You only need to reconfigure it when its configuration changes. ``` glean-idx datasource configure --connector connector:CompanyWikiConnector ``` The CLI rejects invalid datasource names before making a request. If the write loses its connection after being sent, the SDK reads the configuration back without retrying the write. Equivalent state is reported as success; an unreadable or different result is reported as an ambiguous outcome that you must inspect before retrying. For the first live full crawl, use a disposable datasource and verify the target shown by the confirmation prompt. A full live test is a replacement and can stale-delete documents it does not produce, so the explicit acknowledgement is required: ``` glean-idx test --phase live \ --connector connector:create_connector \ --mode full \ --max-items 5 \ --allow-destructive-live ``` Do not rely on `--max-items` to make a shared datasource safe: limiting source consumption makes a full replacement partial, which can delete everything not included. After validating against a disposable datasource, run the same factory for the intended complete crawl with `glean-idx run`. ## Verify it landed[​](#verify-it-landed "Direct link to Verify it landed") Indexing is asynchronous — an accepted upload is not yet a searchable document. Check status from the CLI: ``` glean-idx document status --datasource companywiki --document article page_123 --poll ``` Then search Glean for a phrase from the document body. If it isn't there, work through [Status and debugging](/libraries/indexing-sdk/status-and-debugging.md) rather than re-running blindly. ## Next steps[​](#next-steps "Direct link to Next steps") ### [Connector types](/libraries/indexing-sdk/concepts/connector-types.md) [Streaming and async variants for larger datasets.](/libraries/indexing-sdk/concepts/connector-types.md) ### [Permissions](/libraries/indexing-sdk/permissions.md) [Index documents with real ACLs and verify enforcement.](/libraries/indexing-sdk/permissions.md) ### [Pull integrations](/libraries/indexing-sdk/pull/http-client.md) [HTTP client, pagination, and rate limiting.](/libraries/indexing-sdk/pull/http-client.md) ### [Deployment](/libraries/indexing-sdk/deployment/overview.md) [Run your connector on a schedule.](/libraries/indexing-sdk/deployment/overview.md) ### [Sample dataset](https://github.com/gleanwork/glean-cookbook/tree/main/examples/sample-catalog) [A runnable connector over 29 documents with real ACLs.](https://github.com/gleanwork/glean-cookbook/tree/main/examples/sample-catalog) --- # Status and debugging Indexing is asynchronous. An upload that returns success has been *accepted*, not indexed — documents become searchable some time later. Most "the connector is broken" reports are really one of four things, and this sequence separates them. ## Why isn't my document in search?[​](#why-isnt-my-document-in-search "Direct link to Why isn't my document in search?") Work through it in order. Each step rules out one layer. 1 Did the connector actually upload it? Run against a recording mock and print `result.documents_posted`. If the document isn’t there, the bug is in your data client or `transform()`, not in Glean. Stop here. 2 Has it finished indexing? Run `glean-idx document status --datasource NAME --document TYPE ID --poll`. If it’s still pending, wait — large uploads take longer. 3 Is the datasource healthy? `glean-idx datasource status --datasource NAME` reports uploaded and indexed counts side by side. A gap between them points at rejected documents rather than slow indexing. 4 Can the user see it? If the document is indexed but one person can’t find it, it’s a permissions problem. `glean-idx document access --datasource NAME --object-type TYPE --id ID --user them@example.com` answers it directly — a correct-looking ACL is still wrong if the identities it names were never pushed. ## StatusClient[​](#statusclient "Direct link to StatusClient") Read-only wrappers over the debug endpoints: ``` from glean.indexing.push import StatusClient status = StatusClient(datasource="companywiki") status.get_datasource_status() # counts and datasource state status.get_documents_status(...) # per-document indexing status status.check_document_access(...) # whether a user can access a document ``` The constructor takes the same optional overrides as `PushUploader`: `retries`, `server_url`, `timeout_ms`, `http_headers`. Credentials come from the environment. ## The CLI[​](#the-cli "Direct link to The CLI") The SDK ships one CLI, `glean-idx`. This needs only credentials, so it runs anywhere — including with no install: ``` glean-idx document status \ --datasource companywiki \ --document article page_123 \ --document article page_124 \ --poll ``` `--document` takes two values: object type and document ID. Repeat it for multiple documents. `--poll` checks every 30 seconds for up to five minutes instead of once. In Python: ``` from glean.api_client.models import DebugDocumentRequest from glean.indexing.testing import check_documents_status, poll_documents_status snapshot = poll_documents_status( "companywiki", [DebugDocumentRequest(object_type="article", doc_id="page_123")], ) print(snapshot.result) ``` This is worth wiring into a deployment smoke test — index a known document, then confirm it becomes searchable before declaring the rollout good. ## Common causes[​](#common-causes "Direct link to Common causes") | Symptom | Likely cause | | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `datasource configure` reports an unknown outcome | The connection failed after the write may have committed and read-back could not prove equivalence. Inspect the current configuration with `glean-idx doctor --datasource NAME` before retrying. | | Document never appears | Never uploaded. Check `documents_posted` in a mocked run first. | | Appears, then disappears | A later **full** crawl didn't include it, so it was deleted as stale. | | Indexed but not findable by a user | ACL doesn't cover them, or the identity graph is incomplete. | | Indexed but body is empty | `ContentDefinition` missing, wrong `mime_type`, or empty `text_content`. | | Wrong sort order or "updated" date | `created_at` / `updated_at` passed as ISO strings instead of epoch seconds. | | Upload succeeds, count doesn't grow | Documents rejected server-side. Check `get_datasource_status()`. | ## Documents disappearing is usually stale deletion[​](#documents-disappearing-is-usually-stale-deletion "Direct link to Documents disappearing is usually stale deletion") If documents vanish after a scheduled run, the cause is almost always a full crawl that fetched an incomplete result set. A full crawl deletes anything it didn't include. Look for a partial fetch that was allowed to complete successfully — a source error swallowed inside a paging loop, an auth token expiring mid-crawl, or a `max_items` cap left set from local testing. `max_items` limits source consumption; it is not a blast-radius guarantee, and on a full crawl it can make the replacement dangerously incomplete. Alert on `documents_indexed` dropping sharply between runs; that catches it before the deletion propagates. See [Indexing modes](/libraries/indexing-sdk/concepts/indexing-modes.md). ## Tracing a document through the logs[​](#tracing-a-document-through-the-logs "Direct link to Tracing a document through the logs") With [structured logging](/libraries/indexing-sdk/observability.md) enabled, every crawl carries a `run_id` and lifecycle events are emitted per batch. In your log aggregator, filter by document ID or `run_id` to see whether a document was fetched, transformed, and which batch carried it. For per-document lifecycle events server-side: ``` from glean.indexing.push import PushUploader PushUploader(datasource="companywiki").get_document_lifecycle_events(...) ``` ## Related[​](#related "Direct link to Related") ### [Indexing API debugging](/api-info/indexing/debugging/datasource-status.md) [The underlying debug endpoints.](/api-info/indexing/debugging/datasource-status.md) ### [Observability](/libraries/indexing-sdk/observability.md) [Logging and metrics setup.](/libraries/indexing-sdk/observability.md) --- # End-to-end testing Run this from the CLI `glean-idx test --phase live --mode full --allow-destructive-live` runs a full live phase without writing a test file. It displays the resolved Glean target for confirmation. See the [CLI reference](/libraries/indexing-sdk/cli.md). Phase 3 removes all mocking. The connector fetches from your real source and uploads to whatever `GLEAN_SERVER_URL` points at. ``` from glean.indexing.models import IndexingMode from glean.indexing.testing import TestConfig, TestHarness harness = TestHarness( connector=my_connector, config=TestConfig.from_yaml("testing_config.yaml"), clients={"data_client": real_data_client}, ) result = harness.run_end_to_end( mode=IndexingMode.FULL, confirm=True, allow_destructive=True, confirmed_target="https://test-company-be.glean.com", ) ``` danger **This writes real documents to a real Glean instance.** The harness does not sandbox the datasource: your connector's own datasource name is used as-is, and `run_id_prefix` is logged but does not namespace anything. There is no automatic cleanup. Programmatic runs refuse to start unless `confirm=True` and `allow_destructive=True`; `confirmed_target` also detects a target change between review and execution. Those acknowledgements do not identify production for you. Point `GLEAN_SERVER_URL` at a dedicated test instance and compare it with the confirmed target. ## Before you run it[​](#before-you-run-it "Direct link to Before you run it") * `GLEAN_SERVER_URL` points at a **test** instance, verified by eye, not by memory. * `GLEAN_INDEXING_API_TOKEN` is scoped to the test datasource. * `max_items` is set low in `TestConfig` to limit source consumption. * You know how you'll clean up. Per-client `max_items` limits are applied in Phase 3 too, but they only stop consuming registered source clients after N items. They do not cap Glean-side writes, constrain unregistered clients, or guarantee a small blast radius. With `mode=IndexingMode.FULL`, a low limit creates a partial replacement and can stale-delete every existing document outside that subset. Use a disposable datasource. ## What it returns[​](#what-it-returns "Direct link to What it returns") `run_end_to_end()` returns an `IndexingWaitResult`, or `None` if the connector uploaded no documents. Because indexing is asynchronous, an accepted upload is not yet a searchable document — the result reflects the indexing outcome, not just the HTTP response. ``` result = harness.run_end_to_end( mode=IndexingMode.FULL, confirm=True, allow_destructive=True, confirmed_target="https://test-company-be.glean.com", ) if result is None: raise AssertionError("connector uploaded nothing") ``` ## Verifying documents landed[​](#verifying-documents-landed "Direct link to Verifying documents landed") Poll indexing status for specific documents: ``` from glean.api_client.models import DebugDocumentRequest from glean.indexing.testing import poll_documents_status snapshot = poll_documents_status( "companywiki", [DebugDocumentRequest(object_type="article", doc_id="page_123")], ) print(snapshot.result) ``` `check_documents_status()` is the single-shot variant. Or from the shell: ``` glean-idx document status --datasource companywiki --document article page_123 --poll ``` See [Status and debugging](/libraries/indexing-sdk/status-and-debugging.md). ## Verifying permissions[​](#verifying-permissions "Direct link to Verifying permissions") This is the one check only Phase 3 can make: query Glean as a restricted user and confirm they cannot see documents they shouldn't. ``` from glean.indexing.push import StatusClient status = StatusClient(datasource="companywiki") status.check_document_access(...) ``` Then search as that user. Search results are the ground truth — an upload that succeeded with a correct-looking ACL can still be wrong if the identity graph is incomplete. ## Cleaning up[​](#cleaning-up "Direct link to Cleaning up") Nothing is cleaned up for you. Write teardown that deletes what the test created: ``` from glean.indexing.push import PushUploader uploader = PushUploader(datasource="companywiki") for doc_id in created_ids: uploader.delete_document(object_type="article", document_id=doc_id) ``` The live harness rejects people connectors and datasource connectors that override `get_identities()`, because it cannot automatically reverse identity mutations. Test identity payloads with mocked Glean instead. info There is no API to delete a datasource *registration*. Deleting the test documents leaves the empty datasource registered. Re-running the connector repopulates it. To remove the datasource entirely, use the Glean admin console. ## When to run it[​](#when-to-run-it "Direct link to When to run it") Phase 3 is slow, needs credentials, and mutates real state — so it doesn't belong in a per-commit pipeline. Run it: * Before first deploying a new connector. * After changing `transform()` or the permission model. * On a nightly or pre-release schedule against a test instance. Keep Phases 1 and 2 as the fast feedback loop. --- # Integration testing Run this from the CLI `glean-idx test --phase integration` runs this phase without writing a test file. See the [CLI reference](/libraries/indexing-sdk/cli.md). Phase 2 hits your **real source API** while keeping Glean mocked. The first run records every response to NDJSON on disk; later runs replay from those files without touching the network. That combination is what makes it useful: you validate your data client against responses the source actually produced, then re-run those exact responses forever, offline and deterministically. ``` from glean.indexing.testing import TestConfig, TestHarness harness = TestHarness( connector=my_connector, config=TestConfig.from_yaml("testing_config.yaml"), clients={"data_client": real_data_client}, ) result = harness.run_integration_test() result.assert_documents_posted() ``` ## Registering clients[​](#registering-clients "Direct link to Registering clients") `clients` maps **connector attribute names** to data client instances: ``` harness = TestHarness( connector=my_connector, clients={ "data_client": articles_client, "comments_client": comments_client, }, ) ``` Each key must name a real attribute on the connector; a typo raises `AttributeError` rather than silently skipping that client. Clients you don't register are left untouched — they'll hit the real API on every run without being recorded. ## Configuration[​](#configuration "Direct link to Configuration") `testing_config.yaml` at the connector root, with a top-level `testing:` key: testing\_config.yaml ``` testing: cache_dir: .glean_test_cache/ use_cache: true refresh_cache: false run_id_prefix: sdk_test clients: data_client: max_items: 5 comments_client: max_items: 20 negative_test_identities: - contractor@example.com - external-partners ``` | Key | Default | Purpose | | -------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `cache_dir` | `.glean_test_cache/` | Where NDJSON fixtures are written. | | `use_cache` | `true` | Replay from cache when a valid fixture exists. | | `refresh_cache` | `false` | Force re-recording even on a cache hit. | | `run_id_prefix` | `sdk_test` | Prefix for Phase 3 upload run IDs. | | `clients..max_items` | `5` | Limits source consumption to at most N items for that client. `null` disables the cap. This is not an upload or blast-radius limit. | | `negative_test_identities` | `[]` | Identities that must not be able to access any transformed document. | Load it explicitly, or build one in process: ``` config = TestConfig.from_yaml("testing_config.yaml") config = TestConfig(cache_dir=".glean_test_cache/", use_cache=True) ``` A missing file raises `FileNotFoundError`; a file without the `testing:` key raises `KeyError` naming the keys it did find. ## Record and replay[​](#record-and-replay "Direct link to Record and replay") On the first run, each registered client is wrapped in a recording wrapper that forwards to the real API and writes items to NDJSON. On later runs, a replay wrapper serves those items from disk. To re-record after the source changes, set `config.refresh_cache = True` or delete `cache_dir`. The cache manifest tracks the SDK version used to record. A fixture recorded by a different version won't be replayed silently. warning Recorded fixtures contain **real data from your source**. Review them before committing: they may contain names, email addresses, or internal content. Either scrub them, or add `cache_dir` to `.gitignore` and accept that CI records on first run. ## Keeping fixtures small[​](#keeping-fixtures-small "Direct link to Keeping fixtures small") `max_items` defaults to 5 per client — deliberately small. The point of Phase 2 is validating response *shape*, not volume, and small fixtures stay reviewable and cheap to commit. Raise it for a client whose pagination you specifically want to exercise: ``` clients: articles_client: max_items: 250 # spans several pages ``` The wrapper stops consuming the registered source client after the cap. It does not cap transformed documents at Glean, constrain unregistered clients, or make a full replacement safe. Never treat `max_items` as a blast-radius guarantee. ## Asserting permissions[​](#asserting-permissions "Direct link to Asserting permissions") Identities in `negative_test_identities` are checked against every transformed document after the run. The assertion fails when a configured identity appears literally in `allowed_users`, `allowed_groups`, or any intersection's `required_groups`. It also fails conservatively when `permissions` is missing, `allow_anonymous_access=True`, or `allow_all_datasource_users_access=True`, because the identity's lack of access cannot be established. This is the cheapest real permissions test you can run: it uses production-shaped data, needs no Glean instance, and rejects both literal ACL leaks and broad access before either reaches an index. You can also assert positively: ``` from glean.indexing.testing import extract_permission_refs result = harness.run_integration_test() refs = extract_permission_refs(result.documents_posted) assert "engineering" in refs.group_ids assert "contractor@example.com" not in refs.user_ids ``` ## In CI[​](#in-ci "Direct link to In CI") Commit the fixtures and CI replays them — no source credentials in CI, deterministic runs. If you'd rather not commit real data, run Phase 2 locally only and keep CI on Phase 1. ## What to test here[​](#what-to-test-here "Direct link to What to test here") * Pagination actually terminates, and covers every page. * Fields you assumed were always present sometimes aren't. * Rate limiting and retries behave against real response headers. * Permission payloads map correctly from real ACL data. What Phase 2 still can't tell you is whether Glean accepts and indexes your documents. That's [Phase 3](/libraries/indexing-sdk/testing/end-to-end.md). --- # Testing overview Connector bugs are expensive to find in production: a crawl either indexes nothing, indexes the wrong thing, or — worst case — deletes documents it should have kept. The SDK's testing tools exist so you find those problems before pointing a connector at a real instance. Testing progresses through three phases. Each phase swaps exactly one dependency from mocked to real, so a failure tells you which layer broke. [1Unit](/libraries/indexing-sdk/testing/unit.md) [SourceMocked](/libraries/indexing-sdk/testing/unit.md) [GleanMocked](/libraries/indexing-sdk/testing/unit.md) [SpeedMilliseconds](/libraries/indexing-sdk/testing/unit.md) [CredentialsNone](/libraries/indexing-sdk/testing/unit.md) [run\_connector with a static data client. No network at all.](/libraries/indexing-sdk/testing/unit.md) [Does transform() produce the documents I expect?](/libraries/indexing-sdk/testing/unit.md) [2Integration](/libraries/indexing-sdk/testing/integration.md) [SourceReal](/libraries/indexing-sdk/testing/integration.md) [GleanMocked](/libraries/indexing-sdk/testing/integration.md) [SpeedFast after first run](/libraries/indexing-sdk/testing/integration.md) [CredentialsSource only](/libraries/indexing-sdk/testing/integration.md) [Records real source responses to NDJSON, then replays them offline.](/libraries/indexing-sdk/testing/integration.md) [Does my data client parse what the source really returns?](/libraries/indexing-sdk/testing/integration.md) [3End-to-end](/libraries/indexing-sdk/testing/end-to-end.md) [SourceReal](/libraries/indexing-sdk/testing/end-to-end.md) [GleanReal](/libraries/indexing-sdk/testing/end-to-end.md) [SpeedSlow](/libraries/indexing-sdk/testing/end-to-end.md) [CredentialsSource + Glean](/libraries/indexing-sdk/testing/end-to-end.md) [No mocking. Uploads to whatever GLEAN\_SERVER\_URL points at.](/libraries/indexing-sdk/testing/end-to-end.md) [Does the whole thing work against a live instance?](/libraries/indexing-sdk/testing/end-to-end.md) Most of your tests should be Phase 1. Phase 2 catches the class of bug you can't mock your way to — a source field that's `null` more often than the docs suggest. Phase 3 is a pre-release check, not something you run on every commit. From the CLI, move through the phases explicitly: ``` glean-idx test --phase mock --max-items 5 glean-idx test --phase integration --max-items 5 glean-idx test --phase live --mode full --max-items 5 --allow-destructive-live ``` The CLI's mock phase mocks Glean but leaves the connector's source clients unchanged; use static clients for a no-network Phase 1. Every live phase requires `--allow-destructive-live` and target confirmation. A full live phase replaces the datasource, and `max_items` only limits registered source-client consumption—it is not a Glean-side cap or blast-radius guarantee. ## The harness[​](#the-harness "Direct link to The harness") `TestHarness` wraps a connector and drives all three phases: ``` from glean.indexing.testing import TestConfig, TestHarness harness = TestHarness( connector=my_connector, config=TestConfig(), clients={"data_client": real_data_client}, # required for Phase 2 and 3 ) ``` `clients` maps **connector attribute names** to data client instances. Keys must match real attributes on the connector — `AttributeError` otherwise. Phase 1 doesn't need it. ## Output validation[​](#output-validation "Direct link to Output validation") `run_connector()` and the harness both run `validate_connector_output()` automatically, which catches structural mistakes — a document whose `datasource` doesn't match the connector, a missing required field — and raises `ConnectorOutputValidationError`. You get those checks without writing them. ## Testing permissions[​](#testing-permissions "Direct link to Testing permissions") Every phase can assert on ACLs. Because permissions are the highest-risk part of a connector, test the negative case explicitly — see [Permissions](/libraries/indexing-sdk/permissions.md). --- # Unit testing Run this from the CLI `glean-idx test --phase mock` runs this phase without writing a test file and mocks Glean. It leaves the connector's source clients unchanged, so use static clients when the phase must make no network calls. See the [CLI reference](/libraries/indexing-sdk/cli.md). Phase 1 runs your connector end to end with the Glean API mocked and static data on the source side. No network, no credentials, milliseconds per test. This is where most of your connector tests should live. ## run\_connector[​](#run_connector "Direct link to run_connector") Pair `run_connector` with a static data client: ``` from glean.indexing.testing import StaticDataClient, run_connector result = run_connector( MyConnector("myds", StaticDataClient([ {"id": "1", "title": "Doc 1", "url": "https://example.com/1"}, {"id": "2", "title": "Doc 2", "url": "https://example.com/2"}, ])) ) result.assert_documents_posted(count=2, datasource="myds") ``` It returns a `MockGleanClient` that recorded everything the connector sent. Static clients exist for each connector shape: | Client | Pairs with | | -------------------------------- | ------------------------------------------------ | | `StaticDataClient` | `BaseDatasourceConnector`, `BasePeopleConnector` | | `StaticStreamingDataClient` | `BaseStreamingDatasourceConnector` | | `StaticAsyncStreamingDataClient` | `BaseAsyncStreamingDatasourceConnector` | ## Assertions[​](#assertions "Direct link to Assertions") ``` result.assert_documents_posted(count=2, datasource="myds") result.assert_employees_posted(count=10) result.assert_users_posted(count=5) result.assert_groups_posted(count=2) result.assert_memberships_posted(count=8) result.assert_datasource_configured(name="myds") ``` Omit `count` to assert only that something was posted. For anything more specific, read the recorded objects directly: ``` docs = result.documents_posted assert [d.id for d in docs] == ["1", "2"] assert docs[0].title == "Doc 1" assert docs[0].body.text_content.startswith("Welcome") assert docs[0].updated_at == 1769956200 # epoch seconds, not a string ``` Available collections: `documents_posted`, `employees_posted`, `users_posted`, `groups_posted`, `memberships_posted`. ## Async connectors[​](#async-connectors "Direct link to Async connectors") Use the async runner: ``` import pytest from glean.indexing.testing import StaticAsyncStreamingDataClient, run_connector_async @pytest.mark.asyncio async def test_async_connector(): result = await run_connector_async( MyAsyncConnector("myds", StaticAsyncStreamingDataClient(fixtures)) ) result.assert_documents_posted(count=10) ``` Calling `run_connector()` on an async connector from inside a running event loop raises a `RuntimeError` telling you to use `run_connector_async` — rather than the confusing `asyncio.run() cannot be called from a running event loop` you'd otherwise get. ## Testing modes and options[​](#testing-modes-and-options "Direct link to Testing modes and options") ``` from glean.indexing.models import ConnectorOptions, IndexingMode result = run_connector( connector, mode=IndexingMode.INCREMENTAL, options=ConnectorOptions(force_restart=True), ) ``` ## Driving index\_data yourself[​](#driving-index_data-yourself "Direct link to Driving index_data yourself") When you need to interleave calls or inspect state between them, use the context manager instead: ``` from glean.indexing.testing import mock_glean_client with mock_glean_client() as client: connector.configure_datasource() connector.index_data(mode=IndexingMode.INCREMENTAL) client.assert_datasource_configured(name="myds") client.assert_documents_posted(count=2) ``` The yielded client is a facade over a `MagicMock(spec=Glean)`, so the whole generated client surface is reachable — and typos fail loudly rather than silently passing: ``` client.indexing.documents.bulk_index.assert_called_once() # real method client.indexing.documents.bluk_index # AttributeError ``` `@with_mock_glean_client` is the decorator form; it injects the client as the first positional argument. info `mock_glean_client()` skips the automatic output validation that `run_connector()` performs. Prefer `run_connector()` unless you specifically need manual control. ## Guarding against real network calls[​](#guarding-against-real-network-calls "Direct link to Guarding against real network calls") The SDK's own suite uses `pytest-socket` to fail any test that opens a socket. It's a cheap way to guarantee a "unit" test stayed a unit test: ``` pytest -p no:socket tests/unit ``` ## What to test here[​](#what-to-test-here "Direct link to What to test here") * `transform()` output: field mapping, timestamp conversion, MIME types. * Edge cases in source data: missing optional fields, empty strings, unicode. * Permission mapping — see [Permissions](/libraries/indexing-sdk/permissions.md). * Empty result sets, and that a single malformed record doesn't abort the batch. What you *can't* test here is whether your assumptions about the source's real responses are correct. That's [Phase 2](/libraries/indexing-sdk/testing/integration.md). --- # Third-Party Cookie Management Major browsers like Safari, Firefox, and Chrome (including Chromium-based browsers like Edge) have added or are planning to add restrictions to third-party cookie usage. This change entails replacing third-party cookie usage patterns by web browsers with a new partitioned cookie type or blocking them entirely. ## Browser Cookie Policies[​](#browser-cookie-policies "Direct link to Browser Cookie Policies") info Google Chrome has announced plans to move towards a more privacy-focused web environment with initiatives like the Privacy Sandbox, which aims to provide personalization while protecting user privacy. Google is planning to introduce a new experience in Chrome that lets people make an informed choice that applies across their web browsing, including (but not limited to) third-party cookie usage. Mozilla Firefox and Apple's Safari have already implemented strict third-party cookie blocking by default: * Safari uses Intelligent Tracking Prevention (ITP) to block cookies used by advertisers to track users across sites * Firefox uses Enhanced Tracking Protection (ETP) for similar functionality * Other Chromium-based browsers, like Microsoft Edge, are expected to follow Chrome's implementation ## Impact on Glean Functionality[​](#impact-on-glean-functionality "Direct link to Impact on Glean Functionality") warning Since Glean is embedded on customers' websites, it is classified as a third-party and this change can have an impact on critical workflows. Glean's embedded widgets and other functionalities rely on third-party cookies for authenticating users and these would be affected if third-party cookies are blocked. ## Granting Cookie Access[​](#granting-cookie-access "Direct link to Granting Cookie Access") Glean has an inbuilt flow that requests users for third-party cookie access if needed. After login, you will see the following screen: ![Glean 3P Cookie Access Request](/img/third_party_cookies/3P-cookie-request.png) To grant access: 1. Click on "Allow Cookies" 2. You will see a browser prompt: ![Browser 3P Cookie Access Prompt](/img/third_party_cookies/3P-cookie-browser-prompt.png) Browser prompt for third-party cookie access 3. Click on "Allow" to grant Glean access to third-party cookies 4. Glean will complete the login process successfully ## Frequently Asked Questions[​](#frequently-asked-questions "Direct link to Frequently Asked Questions") How do I grant access if I accidentally clicked 'Block'? If you clicked "Block" when the browser asked for third-party cookie access, any subsequent click on "Allow cookies" will automatically be rejected. To manually grant access: 1. Click on the cookie access icon ![](/img/third_party_cookies/cookie-access-icon.png) in the top-right corner of the URL address bar 2. In the prompt, switch the glean.com toggle to "On": ![Allow Cookies Chrome](/img/third_party_cookies/3P-cookie-chrome-allow.png) 3. Click on "Allow Cookies" in the Glean UI 4. The login should complete successfully Why don't I see a prompt after clicking 'Allow Cookies'? This can occur in two scenarios: 1. Access was previously denied or the prompt was dismissed multiple times (see solution above) 2. The embedding `iframe` configuration needs to meet specific requirements: * If the `iframe` is not sandboxed, there are no issues * If sandboxed, it must include these sandbox attributes: * `allow-storage-access-by-user-activation` * `allow-scripts` * `allow-same-origin` Do I need to grant access for each domain separately? Yes, cookie access is domain-specific. The duration of access varies by browser: * **Google Chrome/Chromium:** Access persists for the current user profile * **Firefox:** Access persists for the current user profile * **Safari:** Access is limited to the current browser tab only --- # SSO Authentication SSO authentication is the default authentication method for the Glean Web SDK. When users interact with Glean components, they see a login button that opens your organization's SSO authentication flow in a popup window. This approach is ideal for enterprise deployments where users already have Glean accounts. ## How It Works[​](#how-it-works "Direct link to How It Works") The SSO authentication flow follows these steps: 1. **User Interaction:** User clicks on a Glean component (search box, chat, etc.) 2. **Login Prompt:** If not authenticated, user sees a login button 3. **SSO Flow:** Clicking the login button opens an SSO authentication popup 4. **Email Entry:** User enters their work email address 5. **SSO Redirect:** User is redirected to your organization's SSO provider 6. **Authentication Complete:** After successful SSO login, the popup closes and the user can access Glean ## Installation & Setup[​](#installation--setup "Direct link to Installation & Setup") 1 Install the Web SDK * NPM * Script Tag ``` npm install @gleanwork/web-sdk ``` Import in your application: ``` import GleanWebSDK from '@gleanwork/web-sdk'; ``` Add to your page's ``: ``` ``` Replace `{GLEAN_APP_DOMAIN}` with your Glean web app domain (e.g., `app.glean.com`). 2 Render a Component SSO authentication is the default - no auth configuration is required: * Search Box * Modal Search * Chat ``` GleanWebSDK.renderSearchBox(document.getElementById('search-container'), { backend: 'https://{your}-be.glean.com/' }); ``` ``` GleanWebSDK.attach(document.getElementById('search-input'), { backend: 'https://{your}-be.glean.com/' }); ``` ``` GleanWebSDK.renderChat(document.getElementById('chat-container'), { backend: 'https://{your}-be.glean.com/' }); ``` ## Optimizing the Authentication Flow[​](#optimizing-the-authentication-flow "Direct link to Optimizing the Authentication Flow") ### Skip Email Entry[​](#skip-email-entry "Direct link to Skip Email Entry") You can bypass the email entry step by providing the `backend` parameter (shown in examples above). This routes users directly to your SSO provider: ``` { backend: 'https://{your}-be.glean.com/' // Skip email entry } ``` When the backend is specified, the authentication flow becomes: 1. User clicks login button 2. Immediately redirected to SSO provider (no email entry) 3. Complete SSO authentication 4. Access Glean ## When to Use SSO Authentication[​](#when-to-use-sso-authentication "Direct link to When to Use SSO Authentication") SSO authentication is ideal when: * ✅ **Users Have Glean Accounts:** Your audience consists of employees or members provisioned in your Glean instance * ✅ **Enterprise Deployments:** You're building internal applications, intranets, or employee-facing tools * ✅ **SSO Compliance Required:** Your security policies require users to authenticate through your SSO provider * ✅ **Third-Party Cookies Enabled:** Users' browsers allow third-party cookies (see warning below) ## Prerequisites[​](#prerequisites "Direct link to Prerequisites") Before implementing SSO authentication: * ☑️ **Users are provisioned** in your Glean instance with active accounts * ☑️ **SSO is configured** for your Glean deployment * ☑️ **Backend URL is known** - your Glean backend domain (format: `{company}-be.glean.com`) ## Complete React Example[​](#complete-react-example "Direct link to Complete React Example") Here's a full example using React: ``` import { useEffect, useRef } from 'react'; import GleanWebSDK from '@gleanwork/web-sdk'; function SearchComponent() { const searchRef = useRef(null); useEffect(() => { if (!searchRef.current) return; // Render search box with SSO auth (default) GleanWebSDK.renderSearchBox(searchRef.current, { backend: 'https://your-server-id-be.glean.com/', searchBoxCustomizations: { placeholderText: 'Search your company...' } }); }, []); return
; } export default SearchComponent; ``` ## Third-Party Cookie Requirements[​](#third-party-cookie-requirements "Direct link to Third-Party Cookie Requirements") warning SSO authentication relies on browser cookies. When third-party cookies are blocked, users will be prompted to enable Glean's access. To avoid user prompts, implement [server-to-server (i.e. token-based) authentication](/libraries/web-sdk/authentication/server-to-server.md) instead. See [Third-Party Cookie Management](/libraries/web-sdk/3rd-party-cookies.md) for details. ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") **Users see login prompt repeatedly:** * Check that third-party cookies are enabled in the browser * Verify your Glean backend URL is correct * Consider implementing [Server-to-Server Authentication](/libraries/web-sdk/authentication/server-to-server.md) for better cross-browser compatibility **Login popup doesn't close:** * Ensure popup blockers are disabled for your domain * Verify SSO configuration in your Glean admin panel * Check browser console for error messages **Users see "Unauthorized" errors:** * Confirm users are provisioned in your Glean instance * Verify SSO is configured correctly * Check that the backend URL matches your deployment ## Related Documentation[​](#related-documentation "Direct link to Related Documentation") * [Authentication Overview](/libraries/web-sdk/authentication/overview.md) * [Server-to-Server Authentication](/libraries/web-sdk/authentication/server-to-server.md) * [Third-Party Cookie Management](/libraries/web-sdk/3rd-party-cookies.md) * [Web SDK Options Reference](/libraries/web-sdk/reference/interfaces/Options.md) --- # Web SDK Authentication The Web SDK provides two authentication methods for integrating Glean's search and chat capabilities into your application. Choose the method that best fits your deployment scenario and user requirements. SSO (default)User clicks widgetSees a sign-in prompt on first useYour identity providerStandard SSO flow in a popupWidget is liveSession persists like the Glean app Server-to-serverYour backendMints a short-lived user token via the Client APIauthToken optionPassed to the SDK, refreshed via callbackWidget is liveNo visible sign-in step for the user ## Authentication Methods[​](#authentication-methods "Direct link to Authentication Methods") ### SSO Authentication (Default)[​](#sso-authentication-default "Direct link to SSO Authentication (Default)") The default authentication method where users complete your organization's SSO login flow. When a user interacts with Glean components, they'll see a login button that opens an SSO authentication flow in a popup window. **Best for:** * Enterprise deployments where users have Glean accounts * Internal applications and intranets * Scenarios requiring SSO compliance **Configuration:** ``` { authMethod: "sso" // This is the default } ``` [Learn more about SSO Authentication →](/libraries/web-sdk/authentication/default-sso.md) ### Token-Based Authentication[​](#token-based-authentication "Direct link to Token-Based Authentication") Your server obtains authentication tokens from Glean's API and provides them to the Web SDK, eliminating the need for users to log in through the SSO flow. This enables seamless authentication for users who may not have Glean accounts or for anonymous access scenarios. **Best for:** * Public-facing applications where users don't have Glean accounts * Seamless authentication without user interaction * Anonymous or guest access to search functionality * Documentation sites and marketing pages **Configuration:** ``` const config = { authMethod: 'token', authToken: 'GLEAN_AUTH_TOKEN_...', onAuthTokenRequired: async () => { // Fetch a new token when the current one expires. return 'GLEAN_AUTH_TOKEN_...'; }, }; ``` [Learn more about Server-to-Server Authentication →](/libraries/web-sdk/authentication/server-to-server.md) ## Quick Decision Guide[​](#quick-decision-guide "Direct link to Quick Decision Guide") | Question | Answer | Recommended Method | | --------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------- | | Do your users have Glean accounts? | Yes | [SSO Authentication](/libraries/web-sdk/authentication/default-sso.md) | | Do you want users to log in via SSO? | Yes | [SSO Authentication](/libraries/web-sdk/authentication/default-sso.md) | | Do you need anonymous/guest access? | Yes | [Token-Based Authentication](/libraries/web-sdk/authentication/server-to-server.md) | | Are you building a public documentation site? | Yes | [Token-Based Authentication](/libraries/web-sdk/authentication/server-to-server.md) | | Do third-party cookies get blocked? | Yes | [Token-Based Authentication](/libraries/web-sdk/authentication/server-to-server.md) (recommended) | ## Getting Started[​](#getting-started "Direct link to Getting Started") ### Installation[​](#installation "Direct link to Installation") Choose your preferred installation method: * NPM * Script Tag ``` npm install @gleanwork/web-sdk ``` Then import in your application: ``` import GleanWebSDK from '@gleanwork/web-sdk'; // Or import specific methods import { attach, renderSearchBox } from '@gleanwork/web-sdk'; ``` Add the Web SDK script to your page's ``: ``` ``` The SDK will be available globally as `window.GleanWebSDK` or just `GleanWebSDK`. Replace `{GLEAN_APP_DOMAIN}` with your Glean web app domain (typically `app.glean.com` or `your-company.glean.com`). ### Basic Usage[​](#basic-usage "Direct link to Basic Usage") Once installed, you can render Glean components with your chosen authentication method: * SSO Auth * Token Auth ``` // No auth configuration needed - SSO is default GleanWebSDK.renderSearchBox(document.getElementById('search-container'), { backend: 'https://{your}-be.glean.com/' }); ``` ``` GleanWebSDK.renderSearchBox(document.getElementById('search-container'), { authMethod: 'token', authToken: yourAuthToken, backend: 'https://{your}-be.glean.com/', onAuthTokenRequired: async () => { const response = await fetch('/api/get-glean-token'); const { token } = await response.json(); return token; } }); ``` ## Third-Party Cookie Considerations[​](#third-party-cookie-considerations "Direct link to Third-Party Cookie Considerations") warning When third-party cookies are blocked in the user's browser, SSO authentication may not work reliably. In these cases, token-based authentication is required for the Web SDK to function properly. See [Third-Party Cookie Management](/libraries/web-sdk/3rd-party-cookies.md) for details. ## Next Steps[​](#next-steps "Direct link to Next Steps") 1. **For standard enterprise deployments:** Start with [SSO Authentication](/libraries/web-sdk/authentication/default-sso.md) to leverage your existing Glean SSO setup 2. **For public-facing sites:** Explore [Server-to-Server Authentication](/libraries/web-sdk/authentication/server-to-server.md) to provide seamless access without requiring user login 3. **Not sure which to choose?** Review the detailed guides for each method to understand the implementation requirements ## Related Documentation[​](#related-documentation "Direct link to Related Documentation") * [Third-Party Cookie Management](/libraries/web-sdk/3rd-party-cookies.md) * [Web SDK Overview](/libraries/web-sdk/overview.md) * [Web SDK Options Reference](/libraries/web-sdk/reference/interfaces/Options.md) *** **Sources:** * [@gleanwork/web-sdk on npm](https://www.npmjs.com/package/@gleanwork/web-sdk) * [Web SDK API Documentation](/libraries/web-sdk/reference/.md) --- # Server-to-Server Authentication Server-to-server authentication enables your backend to provide authentication tokens to the Web SDK, bypassing the need for users to log in through Glean's SSO flow. This approach is essential for scenarios where users don't have Glean accounts or where you want to provide seamless, frictionless access to search and chat functionality. The Web SDK supports two implementation approaches for server-to-server authentication: 1. **Manual Token Management** - Your application fetches and manages auth tokens for authenticated users using a Glean API key or an access token from your OAuth provider 2. **Guest Auth Provider** - The Web SDK automatically manages tokens for anonymous/guest users ## Overview[​](#overview "Direct link to Overview") Unlike [Default SSO Authentication](/libraries/web-sdk/authentication/default-sso.md) where users see a login button and complete an SSO flow, server-to-server authentication (token-based) allows your backend to obtain short-lived authentication tokens on behalf of users. This eliminates the SSO login popup and enables use cases where users may not even have Glean accounts. ## When to Use Server-to-Server Authentication[​](#when-to-use-server-to-server-authentication "Direct link to When to Use Server-to-Server Authentication") Server-to-server authentication is ideal when: * **Users Don't Have Glean Accounts:** Your audience includes external users, customers, or anonymous visitors * **Seamless Authentication Required:** You want to provide access without any user interaction or sign-in prompts * **Public-Facing Applications:** You're building documentation sites, marketing pages, or customer portals * **Anonymous Access Needed:** You want to enable search functionality for guest users * **Third-Party Cookies Are Blocked:** Browser restrictions prevent cookie-based authentication Choose your implementation approach: * Use **Manual Token Management** when you have authenticated users in your system and want full control over token lifecycle * Use **Guest Auth Provider** for public-facing deployments where users don't need to sign in ## Approach 1: Manual Token Management[​](#approach-1-manual-token-management "Direct link to Approach 1: Manual Token Management") This approach gives you complete control over token generation and refresh. Your application manages user authentication and fetches tokens from Glean's API. * API Key * OAuth Access Token ### Prerequisites Before implementing server-to-server authentication, you need an admin API key from Glean: 1. An admin with sufficient access navigates to the [Token Management page](https://app.glean.com/admin/platform/tokenManagement?tab=client) 2. Generate a new API key 3. Store this key securely on your server (e.g., in environment variables) 4. Optionally set an expiration date or rotate the key as needed warning The API key must be stored very securely and **never exposed to client-side code**. It should only be accessible to your backend server. tip The default TTL for generated tokens is 2 hours, but this is configurable. Contact your Glean administrator to adjust this setting if needed. ### Implementation 1 Create Server Endpoint Create a backend endpoint that your client can call to obtain Glean auth tokens. This endpoint calls Glean's `/createauthtoken` API with your admin API key. **Node.js/Express Example:** ``` const express = require("express"); const axios = require("axios"); const app = express(); app.use(express.json()); app.post("/api/get-glean-token", async (req, res) => { const { userEmail } = req.body; // Your backend URL (find at https://app.glean.com/admin/about-glean) const backend = process.env.GLEAN_BACKEND_URL; const apiKey = process.env.GLEAN_API_KEY; const tokenApiPath = backend.endsWith("/") ? `${backend}rest/api/v1/createauthtoken` : `${backend}/rest/api/v1/createauthtoken`; try { const response = await axios({ method: "POST", url: tokenApiPath, headers: { Authorization: `Bearer ${apiKey}`, "X-Scio-ActAs": userEmail, accept: "application/json", }, }); // Response contains: { token: "GLEAN_AUTH_TOKEN_...", expirationTime: 1234567890 } res.json(response.data); } catch (error) { console.error("Failed to fetch Glean token:", error); res.status(500).json({ error: "Failed to generate auth token" }); } }); app.listen(3000); ``` **Key points:** * `Authorization: Bearer ` - Your admin API key from Prerequisites * `X-Scio-ActAs: ` - The authenticated user's email address * Response includes both `token` (the auth token) and `expirationTime` (Unix timestamp in seconds) For more details on this API, see [Create Authentication Token API](/api/client-api/authentication/createauthtoken.md). 2 Install Web SDK Install the Glean Web SDK in your frontend application: * Script Tag * NPM Add to your page's ``: ``` ``` The SDK will be available globally as `GleanWebSDK`. ``` npm install @gleanwork/web-sdk ``` Import in your application: ``` import GleanWebSDK from '@gleanwork/web-sdk'; ``` 3 Implement Client-Side Authentication Fetch a token from your server endpoint and pass it to the Web SDK: ``` // Fetch initial token from your server async function getGleanToken() { const response = await fetch('/api/get-glean-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userEmail: 'user@company.com' // The authenticated user's email }) }); const data = await response.json(); return data; // Returns { token, expirationTime } } // Initialize Web SDK with token-based auth const authToken = await getGleanToken(); GleanWebSDK.renderSearchBox(document.getElementById('search-container'), { authMethod: 'token', authToken: authToken, backend: 'https://{your}-be.glean.com/', onAuthTokenRequired: async () => { // Called when token is approaching expiration return await getGleanToken(); } }); ``` **Key points:** * `authMethod: 'token'` - Use token-based authentication * `authToken` - Initial token object with `token` and `expirationTime` * `onAuthTokenRequired` - Callback invoked when token needs refresh (automatically called \~30 min before expiration) You can use this pattern with any Web SDK component: `renderSearchBox()`, `renderChat()`, `renderSearchResults()`, `attach()`, etc. ### Implementation Checklist * Generate and securely store API key from Glean admin panel * Create backend endpoint (`/api/get-glean-token`) that calls Glean's `/createauthtoken` API * Install Glean Web SDK in your frontend application * Implement client-side token fetching and pass to Web SDK * Implement `onAuthTokenRequired` callback for automatic token refresh * Test token expiration and refresh flow (tokens expire after 2 hours by default) ### Prerequisites Before implementing server-to-server authentication using OAuth access tokens, you need to configure Glean with information about your OAuth provider: 1. Use Google Workspace, Microsoft Entra ID, Okta, or OneLogin as your OAuth provider 2. An admin with sufficient access navigates to [Client API Settings](https://app.glean.com/admin/platform/tokenManagement?tab=client) 3. Enable OAuth token-based access and provide the necessary SSO information tip The default TTL for Glean generated tokens is 2 hours, but this is configurable. Contact your Glean administrator to adjust this setting if needed. ### Implementation 1 Create Server Endpoint Create a backend endpoint that your client can call to obtain Glean auth tokens. This endpoint calls Glean's `/createauthtoken` API with the user's OAuth access token. **Node.js/Express Example:** ``` const express = require("express"); const axios = require("axios"); const app = express(); app.use(express.json()); app.post("/api/get-glean-token", async (req, res) => { const { userEmail } = req.body; // Retrieve the user's OAuth access token from the incoming request. const accessToken = req.headers.authorization?.replace(/^Bearer\s+/i, ""); if (!accessToken) { return res.status(401).json({ error: "Missing OAuth access token" }); } // Your backend URL (find at https://app.glean.com/admin/about-glean) const backend = process.env.GLEAN_BACKEND_URL; const tokenApiPath = backend.endsWith("/") ? `${backend}rest/api/v1/createauthtoken` : `${backend}/rest/api/v1/createauthtoken`; try { const response = await axios({ method: "POST", url: tokenApiPath, headers: { Authorization: `Bearer ${accessToken}`, "X-Glean-Auth-Type": "OAUTH", accept: "application/json", }, }); // Response contains: { token: "GLEAN_AUTH_TOKEN_...", expirationTime: 1234567890 } res.json(response.data); } catch (error) { console.error("Failed to fetch Glean token:", error); res.status(500).json({ error: "Failed to generate auth token" }); } }); app.listen(3000); ``` **Key points:** * `Authorization: Bearer ` - The user's OAuth access token * `X-Glean-Auth-Type: OAUTH` - **Required header** * Response includes both `token` (the auth token) and `expirationTime` (Unix timestamp in seconds) For more details on this API, see [Create Authentication Token API](/api/client-api/authentication/createauthtoken.md). 2 Install Web SDK Install the Glean Web SDK in your frontend application: * Script Tag * NPM Add to your page's ``: ``` ``` The SDK will be available globally as `GleanWebSDK`. ``` npm install @gleanwork/web-sdk ``` Import in your application: ``` import GleanWebSDK from '@gleanwork/web-sdk'; ``` 3 Implement Client-Side Authentication Fetch a token from your server endpoint and pass it to the Web SDK: ``` // Fetch initial token from your server async function getGleanToken() { const response = await fetch('/api/get-glean-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userEmail: 'user@company.com' // The user's email, or other identifier used to retrieve their access token }) }); const data = await response.json(); return data; // Returns { token, expirationTime } } // Initialize Web SDK with token-based auth const authToken = await getGleanToken(); GleanWebSDK.renderSearchBox(document.getElementById('search-container'), { authMethod: 'token', authToken: authToken, backend: 'https://{your}-be.glean.com/', onAuthTokenRequired: async () => { // Called when token is approaching expiration return await getGleanToken(); } }); ``` **Key points:** * `authMethod: 'token'` - Use token-based authentication * `authToken` - Initial token object with `token` and `expirationTime` * `onAuthTokenRequired` - Callback invoked when token needs refresh (automatically called \~30 min before expiration) You can use this pattern with any Web SDK component: `renderSearchBox()`, `renderChat()`, `renderSearchResults()`, `attach()`, etc. ### Implementation Checklist * Configure Glean with all your OAuth provider's details * Create backend endpoint (`/api/get-glean-token`) that calls Glean's `/createauthtoken` API with the user's access token * Install Glean Web SDK in your frontend application * Implement client-side token fetching and pass to Web SDK * Implement `onAuthTokenRequired` callback for automatic token refresh * Test token expiration and refresh flow (tokens expire after 2 hours by default) ## Approach 2: Guest Auth Provider[​](#approach-2-guest-auth-provider "Direct link to Approach 2: Guest Auth Provider") For public-facing deployments where users don't need to authenticate, the Web SDK provides a `GuestAuthProvider` that automatically handles token generation and refresh for anonymous users. ### How It Works[​](#how-it-works "Direct link to How It Works") The Guest Auth Provider: 1. Automatically requests authentication tokens from your Glean backend 2. Caches tokens for reuse 3. Handles token refresh transparently 4. Requires minimal configuration ### Setup Process[​](#setup-process "Direct link to Setup Process") 1 Import the Guest Auth Provider ``` import { createGuestAuthProvider } from '@gleanwork/web-sdk'; ``` 2 Create the Provider ``` const authProvider = createGuestAuthProvider({ backend: 'https://{your}-be.glean.com/' }); ``` The `backend` parameter should match your Glean external search deployment URL. 3 Use with Web SDK ##### Option A: Direct Token Usage[​](#option-a-direct-token-usage "Direct link to Option A: Direct Token Usage") ``` // Get the auth token const token = await authProvider.getAuthToken(); // Render a component with the token GleanWebSDK.renderSearchBox(document.getElementById('search-container'), { authMethod: 'token', authToken: token, backend: 'https://{your}-be.glean.com/', onAuthTokenRequired: async () => { return await authProvider.getAuthToken(); } }); ``` ##### Option B: React Integration[​](#option-b-react-integration "Direct link to Option B: React Integration") Here's a complete example using React context for guest authentication: ``` import { createContext, useContext, useEffect, useState } from 'react'; import { createGuestAuthProvider } from '@gleanwork/web-sdk'; interface GuestAuthContextType { token: string | null; loading: boolean; error: Error | null; refreshToken: () => Promise; } const GuestAuthContext = createContext(null); export function GuestAuthProvider({ children, backend }: { children: React.ReactNode; backend: string; }) { const [token, setToken] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [provider, setProvider] = useState(null); useEffect(() => { const initProvider = async () => { try { const authProvider = createGuestAuthProvider({ backend }); setProvider(authProvider); const authToken = await authProvider.getAuthToken(); setToken(authToken); } catch (err) { setError(err as Error); } finally { setLoading(false); } }; initProvider(); }, [backend]); const refreshToken = async () => { if (!provider) return; try { setLoading(true); const newToken = await provider.createAuthToken(); setToken(newToken); setError(null); } catch (err) { setError(err as Error); } finally { setLoading(false); } }; return ( {children} ); } export function useGuestAuth() { const context = useContext(GuestAuthContext); if (!context) { throw new Error('useGuestAuth must be used within GuestAuthProvider'); } return context; } ``` 4 Apply to Glean Components ``` import { useEffect, useRef } from 'react'; import GleanWebSDK from '@gleanwork/web-sdk'; function SearchComponent() { const { token, loading } = useGuestAuth(); const searchRef = useRef(null); useEffect(() => { if (!token || loading || !searchRef.current) return; GleanWebSDK.renderSearchBox(searchRef.current, { authMethod: 'token', authToken: token, backend: 'https://{your}-be.glean.com/', onAuthTokenRequired: async () => { const authProvider = createGuestAuthProvider({ backend: 'https://{your}-be.glean.com/' }); return await authProvider.getAuthToken(); } }); }, [token, loading]); if (loading) return
Loading...
; return
; } ``` ### Guest Auth Provider API[​](#guest-auth-provider-api "Direct link to Guest Auth Provider API") The `GuestAuthProvider` class provides two key methods: * **`getAuthToken()`**: Returns an existing cached token or creates a new one if none exists * **`createAuthToken()`**: Always creates a fresh authentication token Both methods return a `Promise`. ### When to Use Guest Auth[​](#when-to-use-guest-auth "Direct link to When to Use Guest Auth") Guest authentication is ideal for: * **Public documentation sites** - Allow visitors to search without signing in * **Marketing pages** - Provide search functionality to anonymous users * **Demo environments** - Quick setup without user management * **Internal tools with anonymous access** - Public-facing internal resources ### Implementation Checklist[​](#implementation-checklist "Direct link to Implementation Checklist") * Ensure your Glean deployment supports external search * Configure your backend URL * Import and create the guest auth provider * Integrate with your Web SDK initialization * Test token generation and refresh * Handle loading and error states in your UI ## Comparison: Manual vs Guest Auth[​](#comparison-manual-vs-guest-auth "Direct link to Comparison: Manual vs Guest Auth") | Feature | Manual Token Management | Guest Auth Provider | | -------------------- | ------------------------------------------- | ------------------------------- | | **Use Case** | Authenticated users with known identity | Anonymous/guest users | | **Setup Complexity** | Higher - requires API key & server endpoint | Lower - client-side only | | **Token Control** | Full control over token lifecycle | Automatic management | | **User Experience** | Personalized to authenticated user | Generic guest access | | **Security** | API key must be secured server-side | No sensitive credentials needed | | **Best For** | Internal apps, user-specific content | Public docs, marketing sites | ## Related Documentation[​](#related-documentation "Direct link to Related Documentation") * [Authentication Overview](/libraries/web-sdk/authentication/overview.md) * [Default SSO Authentication](/libraries/web-sdk/authentication/default-sso.md) * [Create Authentication Token API](/api/client-api/authentication/createauthtoken.md) * [Web SDK Options Reference](/libraries/web-sdk/reference/interfaces/Options.md) * [GuestAuthProvider API](/libraries/web-sdk/reference/interfaces/GuestAuthProvider.md) * [GuestAuthProviderOptions](/libraries/web-sdk/reference/interfaces/GuestAuthProviderOptions.md) --- # Search Box + Results Compose a full search experience from two coordinated components: an autocomplete search box (`renderSearchBox`) and a tabbed results page (`renderSearchResults`). This approach gives you the deepest UI control — you own the page layout, Glean renders the search surfaces. PreviewLive — your instance portal.sample.internal/search payments CPayments Service — Deploy & Rollback RunbookEngineering Wiki · Updated 2 days ago Gpayments-servicegit.internal/payments-service · 1mo ago from:teammate type:bug, document, message, etc. ⏎ Enter Search All filtersFrom: meUpdated AllCGSJ C Payments Service — Deploy & Rollback RunbookPriya Natarajan · Updated 2 days ago · Engineering WikiCanary the payments service behind the feature gate, then promote once error rates hold under 0.1% for 15 minutes… G payments-serviceRepository · 42 open pull requestsOwns charge creation, refunds, and ledger writes. See the runbook before deploying to production… S \#payments-oncall — canary alarm follow-upMarcus Webb · YesterdayThe alarm cleared after we rolled back — updating the runbook with the new dashboard links… Illustrative previewRendered with sample data — in your app, this component renders live against your organization's Glean instance. Try it live → ## Install the SDK[​](#install-the-sdk "Direct link to Install the SDK") * npm * Script tag ``` npm install @gleanwork/web-sdk ``` Include the JavaScript library in the `` section of your page. Replace `GLEAN_APP_DOMAIN` with your company's Glean web app domain (typically `app.glean.com` or `your-company.glean.com` if you use a custom subdomain). ``` ``` info The Glean web app domain differs from your company's Glean backend domain (find yours at [app.glean.com/admin/about-glean](https://app.glean.com/admin/about-glean) under "Server instance (QE)"). The same methods are available on `window.GleanWebSDK` once the `glean:ready` event fires. ## Render the search page[​](#render-the-search-page "Direct link to Render the search page") 1. Create two container elements — one for the search box, one for results — each with `position: relative`, `display: block`, and appropriate sizing. Sizing the search box container The search box fills its container (minus any margins from `searchBoxCustomizations`), so size the container to the box itself — around 50–90px tall — not to the dropdown. The autocomplete dropdown renders below the box and extends past the container's bounds, so make sure no ancestor clips it with `overflow: hidden`, and give the search box container a higher `z-index` than the content beneath it so the dropdown overlays that content instead of rendering behind it. 2. Render both components, and connect them through their `onSearch` callbacks so a query typed in the box re-renders the results: ``` import { renderSearchBox, renderSearchResults } from '@gleanwork/web-sdk'; const boxElement = document.getElementById('search-box'); const resultsElement = document.getElementById('search-results'); let currentQuery = ''; function onSearch(query) { currentQuery = query; renderAll(); } function renderAll() { renderSearchBox(boxElement, { onSearch, query: currentQuery }); renderSearchResults(resultsElement, { onSearch, query: currentQuery }); } renderAll(); ``` In a real app you would typically also update the page URL from `onSearch` so searches are linkable and the back button works. ## Options[​](#options "Direct link to Options") The search box accepts [`SearchBoxOptions`](/libraries/web-sdk/reference/interfaces/SearchBoxOptions.md) and the results page accepts [`TabbedSearchOptions`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md). Commonly used options: | Option | Applies to | Type | Description | | ------------------------- | ---------- | ------------------------- | ------------------------------------------------------------------------ | | `onSearch` | Both | `(query) => void` | **Required.** Invoked when the user performs a search. | | `query` | Both | `string` | The query text to perform/display. | | `autofocus` | Search box | `boolean` | Focus the box when it first renders. Defaults to `false`. | | `searchBoxCustomizations` | Search box | `SearchBoxCustomizations` | Placeholder text, font size, icon, borders, and margins. | | `hideAutocomplete` | Search box | `boolean` | Suppress the autocomplete dropdown. | | `datasourcesFilter` | Both | `string[]` | Restrict results to specific datasources, e.g. `['jira', 'confluence']`. | | `filters` | Both | `FilterValue[]` | Pinned, non-modifiable filters applied to every search. | | `showInlineSearchBox` | Results | `boolean` | Render a search box inline above the results. | | `autoExpand` | Results | `boolean` | Grow the results frame to fit content instead of scrolling internally. | | `hideFiltersColumn` | Results | `boolean` | Hide the righthand filter column. | | `onDatasourceChange` | Results | `(datasource) => void` | Invoked when the user switches result tabs. | All components also accept the common [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) — `backend`, `authToken`, `theme`, `themeVariant`, `locale`, and more. ## Theming[​](#theming "Direct link to Theming") Customize the search box appearance with [SearchBoxCustomizations](/libraries/web-sdk/reference/interfaces/SearchBoxCustomizations.md). The theming system is regularly expanded with additional options based on customer needs. --- # Glean Chat Glean Chat delivers personalized answers grounded in your company's content, people, and activity — with citations back to the source. Responses are automatically tailored to what each signed-in user is allowed to see. A complete list of Glean Chat's capabilities can be found in our [Chat documentation](https://docs.glean.com/user-guide/assistant/glean-chat). PreviewLive — your instance portal.sample.internal/assistant New chat Library AgentsTodayWho owns the payments…Canary alarm follow-up Who owns the payments service?Share The payments service is owned by the Payments Platform team. Priya Natarajan is the tech lead, and Marcus Webb is on call this week — the deploy and rollback runbook lives in the Engineering Wiki. Ask a follow-up… Illustrative previewRendered with sample data — in your app, this component renders live against your organization's Glean instance. Try it live → ## Install the SDK[​](#install-the-sdk "Direct link to Install the SDK") * npm * Script tag ``` npm install @gleanwork/web-sdk ``` Include the JavaScript library in the `` section of your page. Replace `GLEAN_APP_DOMAIN` with your company's Glean web app domain (typically `app.glean.com` or `your-company.glean.com` if you use a custom subdomain). ``` ``` info The Glean web app domain differs from your company's Glean backend domain (find yours at [app.glean.com/admin/about-glean](https://app.glean.com/admin/about-glean) under "Server instance (QE)"). The same methods are available on `window.GleanWebSDK` once the `glean:ready` event fires. ## Render chat[​](#render-chat "Direct link to Render chat") 1. Create a container element with `position: relative`, `display: block`, and a defined `width` and `height`. 2. Render Glean Chat into your container: ``` import { renderChat } from '@gleanwork/web-sdk'; renderChat(document.getElementById('chat'), { backend: 'https://{your}-be.glean.com/', }); ``` For the full surface, see the [renderChat API documentation](/libraries/web-sdk/reference/interfaces/GleanWebSDK.md#renderchat). ## Options[​](#options "Direct link to Options") The most commonly used [`ChatOptions`](/libraries/web-sdk/reference/interfaces/ChatOptions.md): | Option | Type | Description | | ---------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `agentId` | `string` | The ID of the [Glean Agent](/guides/agents/overview.md) to chat with. | | `agent` | `'DEFAULT' \| 'GPT' \| 'FAST' \| 'ADVANCED'` | Response engine. `FAST` and `ADVANCED` use the agentic engine; `DEFAULT` and `GPT` are being deprecated in their favor. | | `initialMessage` | `string` | A message sent on the user's behalf to start the conversation. | | `chatId` | `string` | Opaque ID that restores an existing chat session. | | `landingPage` | `'chat' \| 'agentLibrary'` | Initial view when the widget loads. Defaults to `'chat'`. | | `promptId` | `string` | Opens the widget with a saved Glean prompt. | | `customizations` | `ChatCustomizations` | Toggle individual UI features (agent library, prompt library, feedback, chat menu, and more). | All components also accept the common [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) — `backend`, `authToken`, `theme`, `themeVariant`, `locale`, and more. info To chat with a specific [Glean Agent](/guides/agents/overview.md), pass its ID via the `agentId` chat option. The older `applicationId` parameter (for AI Applications built with the deprecated AI App Builder) is still accepted for existing apps, but new integrations should use `agentId` — apps were migrated to agents. ## Examples[​](#examples "Direct link to Examples") ### Separate pages for chat and search[​](#separate-pages-for-chat-and-search "Direct link to Separate pages for chat and search") Dedicated routes for Search and Chat — a Search page with the Glean Search component, a Chat page with the Glean Chat component, and automatic redirection to the Chat page when starting a new conversation. View the complete demo in our [CodeSandbox example](https://codesandbox.io/s/github/askscio/glean-browser-api/tree/main/examples/search-chat-separate-routes). ### Unified page[​](#unified-page "Direct link to Unified page") Search and Chat combined on a single route, toggled with a `mode` query parameter (`search` or `chat`). View the complete demo in our [CodeSandbox example](https://codesandbox.io/s/github/askscio/glean-browser-api/tree/main/examples/search-chat-unified). --- # Modal Search Modal Search overlays a complete Glean search experience on your existing page. Attach it to any search input you already have — when the input is clicked, a modal opens with autocomplete, suggestions, and full results. PreviewLive — your instance portal.sample.internal Sample CorpServicesRunbooksOn-call payments service runbook CPayments Service — Deploy & Rollback RunbookEngineering Wiki · Updated 2 days ago Gpayments-servicegit.internal/payments-service · 1mo ago from:teammate type:bug, document, message, etc. ⏎ Enter Search powered by Glean Illustrative previewRendered with sample data — in your app, this component renders live against your organization's Glean instance. Try it live → ## Install the SDK[​](#install-the-sdk "Direct link to Install the SDK") * npm * Script tag ``` npm install @gleanwork/web-sdk ``` Include the JavaScript library in the `` section of your page. Replace `GLEAN_APP_DOMAIN` with your company's Glean web app domain (typically `app.glean.com` or `your-company.glean.com` if you use a custom subdomain). ``` ``` info The Glean web app domain differs from your company's Glean backend domain (find yours at [app.glean.com/admin/about-glean](https://app.glean.com/admin/about-glean) under "Server instance (QE)"). The same methods are available on `window.GleanWebSDK` once the `glean:ready` event fires. ## Attach to your search input[​](#attach-to-your-search-input "Direct link to Attach to your search input") Point the [attach](/libraries/web-sdk/reference/interfaces/GleanWebSDK.md#attach) method at a focusable element — such as the search input your page already renders — and the SDK handles all user interaction from there: ``` import { attach } from '@gleanwork/web-sdk'; attach(document.getElementById('search-box'), { backend: 'https://{your}-be.glean.com/', }); ``` ## Options[​](#options "Direct link to Options") The most commonly used [`ModalSearchOptions`](/libraries/web-sdk/reference/interfaces/ModalSearchOptions.md): | Option | Type | Description | | ------------------------- | ------------------------- | --------------------------------------------------------------------------- | | `query` | `string` | If non-empty, the modal opens immediately with results for this query. | | `onSearch` | `(query) => void` | Invoked when the user performs a search (e.g. for tracking). | | `onDetach` | `() => void` | Invoked when the modal is dismissed and focus returns to your native input. | | `showNativeSearchToggle` | `boolean` | Offer users an option to revert to your native search experience. | | `searchBoxCustomizations` | `SearchBoxCustomizations` | Placeholder text, font size, icon, borders, and margins. | | `datasourcesFilter` | `string[]` | Restrict results to specific datasources, e.g. `['jira', 'confluence']`. | All components also accept the common [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) — `backend`, `authToken`, `theme`, `themeVariant`, `locale`, and more. ## Theming[​](#theming "Direct link to Theming") Customize the appearance of Modal Search with [SearchBoxCustomizations](/libraries/web-sdk/reference/interfaces/SearchBoxCustomizations.md). The theming system is regularly expanded with additional options based on customer needs. --- # Recommendations The Recommendations component surfaces intelligent, context-aware suggestions for the page the user is currently viewing, with a built-in Glean search box for manual searches. PreviewLive — your instance portal.sample.internal/runbooks/payments Search for anything at Glean Recommended for this page C Checkout — On-call RunbookUpdated 4 days ago J PAY-2114: Canary alarms during deployIn review P Priya Natarajan — Payments PlatformTech lead Illustrative previewRendered with sample data — in your app, this component renders live against your organization's Glean instance. Try it live → ## Install the SDK[​](#install-the-sdk "Direct link to Install the SDK") * npm * Script tag ``` npm install @gleanwork/web-sdk ``` Include the JavaScript library in the `` section of your page. Replace `GLEAN_APP_DOMAIN` with your company's Glean web app domain (typically `app.glean.com` or `your-company.glean.com` if you use a custom subdomain). ``` ``` info The Glean web app domain differs from your company's Glean backend domain (find yours at [app.glean.com/admin/about-glean](https://app.glean.com/admin/about-glean) under "Server instance (QE)"). The same methods are available on `window.GleanWebSDK` once the `glean:ready` event fires. ## Render recommendations[​](#render-recommendations "Direct link to Render recommendations") 1. Create a container element with `position: relative`, `display: block`, and a defined width (800px maximum recommended — the widget adjusts to its container). 2. Render the widget into your container: ``` import { renderRecommendations } from '@gleanwork/web-sdk'; renderRecommendations(document.getElementById('recommendations'), { backend: 'https://{your}-be.glean.com/', height: 700, customizations: { border: '1px solid grey', borderRadius: 4, horizontalMargin: 3, verticalMargin: 3, searchBox: { placeholderText: 'Search for anything...', }, }, }); ``` ## Options[​](#options "Direct link to Options") The most commonly used [`RecommendationsOptions`](/libraries/web-sdk/reference/interfaces/RecommendationsOptions.md): | Option | Type | Description | | ------------------- | ---------------------------------- | ------------------------------------------------------------------------- | | `height` | `number` | Total widget height in the open state, in pixels. Defaults to `650`. | | `customizations` | `RecommendationsBoxCustomizations` | Border, shadow, margins, search-box styling, and the empty-state hint. | | `sourceDocument` | `DocumentContext` | The document to recommend against. Defaults to the embedding page itself. | | `onSearch` | `(query) => void` | Invoked when the user performs a search from the built-in box. | | `datasourcesFilter` | `string[]` | Restrict results to specific datasources, e.g. `['jira', 'confluence']`. | All components also accept the common [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) — `backend`, `authToken`, `theme`, `themeVariant`, `locale`, and more. note When adding a `boxShadow`, provide enough `verticalMargin` and `horizontalMargin` for it to be visible — margin values must be positive and apply to both sides of the component. --- # Glean Settings Embed Glean settings in your internal app so users can connect datasources that require separate authentication (for example, Slack RTS or GitHub), letting Glean access their private data on their behalf. Beta `renderSettings` is marked experimental in the SDK's type definitions and may change at any time. PreviewLive — your instance portal.sample.internal/settings Search connectors Connected View and manage the connectors available. GGitHub JJira SSlackConnect CConfluenceConnect Illustrative previewRendered with sample data — in your app, this component renders live against your organization's Glean instance. Try it live → ## Install the SDK[​](#install-the-sdk "Direct link to Install the SDK") * npm * Script tag ``` npm install @gleanwork/web-sdk ``` Include the JavaScript library in the `` section of your page. Replace `GLEAN_APP_DOMAIN` with your company's Glean web app domain (typically `app.glean.com` or `your-company.glean.com` if you use a custom subdomain). ``` ``` info The Glean web app domain differs from your company's Glean backend domain (find yours at [app.glean.com/admin/about-glean](https://app.glean.com/admin/about-glean) under "Server instance (QE)"). The same methods are available on `window.GleanWebSDK` once the `glean:ready` event fires. ## Render settings[​](#render-settings "Direct link to Render settings") 1. Create a container element with `position: relative`, `display: block`, and a defined size — at least 800px wide and 600px tall is recommended. 2. Render the settings widget into your container: ``` import GleanWebSDK from '@gleanwork/web-sdk'; GleanWebSDK.renderSettings(document.getElementById('settings'), { backend: 'https://{your}-be.glean.com/', }); ``` note Unlike the other components, `renderSettings` is not exported individually — call it on the SDK's default export (or on `window.GleanWebSDK` when using the script tag). For the full surface, see the [renderSettings API documentation](/libraries/web-sdk/reference/interfaces/GleanWebSDK.md#rendersettings). ## Options[​](#options "Direct link to Options") [`SettingsOptions`](/libraries/web-sdk/reference/interfaces/SettingsOptions.md): | Option | Type | Description | | ------------------------------- | --------- | ------------------------------------------------------------------------- | | `hideUnconfigurableDatasources` | `boolean` | Hide admin-managed datasources that the user cannot configure themselves. | All components also accept the common [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) — `backend`, `authToken`, `theme`, `themeVariant`, `locale`, and more. warning Browser security features can prevent the OAuth popup from informing the SDK that a user has successfully authenticated. For the highest reliability, use the Glean web app or the [`checkdatasourceauth`](/api/client-api/authentication/checkdatasourceauth.md) API. --- # Sidebar Search Deprecated `openSidebar` is marked deprecated in the SDK's type definitions and will be removed in a future version. For new integrations, consider the [Recommendations](/libraries/web-sdk/components/recommendations.md) component for contextual suggestions or [Modal Search](/libraries/web-sdk/components/modal-search.md) for on-demand search. The Glean Sidebar opens a Glean Assistant panel — with chat and search — that slides out from the right side of your page. PreviewLive — your instance portal.sample.internal/runbooks/payments Sample CorpServicesRunbooksOn-call ChatSearch Ask Assistant anything Reference current page Ask Assistant anything… Illustrative previewRendered with sample data — in your app, this component renders live against your organization's Glean instance. Try it live → ## Install the SDK[​](#install-the-sdk "Direct link to Install the SDK") * npm * Script tag ``` npm install @gleanwork/web-sdk ``` Include the JavaScript library in the `` section of your page. Replace `GLEAN_APP_DOMAIN` with your company's Glean web app domain (typically `app.glean.com` or `your-company.glean.com` if you use a custom subdomain). ``` ``` info The Glean web app domain differs from your company's Glean backend domain (find yours at [app.glean.com/admin/about-glean](https://app.glean.com/admin/about-glean) under "Server instance (QE)"). The same methods are available on `window.GleanWebSDK` once the `glean:ready` event fires. ## Open the sidebar[​](#open-the-sidebar "Direct link to Open the sidebar") Call [openSidebar](/libraries/web-sdk/reference/interfaces/GleanWebSDK.md#opensidebar) in response to a user action, such as clicking a button: ``` import { openSidebar } from '@gleanwork/web-sdk'; openSidebar({ backend: 'https://{your}-be.glean.com/', }); ``` The sidebar closes when the user clicks its close button or presses the escape key. ## Options[​](#options "Direct link to Options") `openSidebar` accepts [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md): | Option | Type | Description | | ------------------- | ----------------- | ------------------------------------------------------------------------ | | `query` | `string` | If provided, the sidebar opens showing results for this query. | | `onSearch` | `(query) => void` | Invoked when the user performs a search. | | `datasourcesFilter` | `string[]` | Restrict results to specific datasources, e.g. `['jira', 'confluence']`. | | `filters` | `FilterValue[]` | Pinned, non-modifiable filters applied to every search. | All components also accept the common [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) — `backend`, `authToken`, `theme`, `themeVariant`, `locale`, and more. --- # Brightspot The following steps will guide you through replacing your Brightspot site's native search with Glean's modal search interface. 1 Access Site Settings Navigate to **Admin > Sites & Settings** in your Brightspot dashboard and select the site(s) where you want to implement Glean search. 2 Navigate to Front-end Settings In the **Edit Site** view, select the **Front-end** tab and expand the **Advanced** section. 3 Add Custom Head Elements Container Click **Add Custom Head Elements**, then set **Internal Name** to `Glean`. 4 Configure Web SDK Script Under **Elements**, add a Script Element with the following configuration: | Field | Value | Required | | ----------------- | ---------------------------------------------------------- | -------- | | **Internal Name** | `Glean Web SDK` | ✅ | | **Type** | `Link` | ✅ | | **Script URL** | `https://{GLEAN_APP_DOMAIN}/embedded-search-latest.min.js` | ✅ | | **Defer** | `true` | ✅ | Replace `{GLEAN_APP_DOMAIN}` with your company's Glean app domain, such as `app.glean.com`. 5 Add Integration Script Add another Script Element with these settings: | Field | Value | Required | | ----------------- | ---------------------- | -------- | | **Internal Name** | `Glean Search Replace` | ✅ | | **Type** | `Inline` | ✅ | | **Body** | See script below | ✅ | ``` addEventListener('DOMContentLoaded', () => { // If you've customized the page header, you'll need to adjust these selectors. GleanWebSDK.attach(document.querySelector('.Page-header-search-open')); GleanWebSDK.attach(document.querySelector('.Page-header-search-input')); }) ``` 6 Save Changes Click **Save** to apply your configuration changes. note If you have customized your page header, you may need to modify the CSS selectors in the integration script to match your specific implementation. --- # LumApps To integrate Glean's modal search interface into your LumApps environment, add custom code to your site's ``. In the LumApps administration UI, open **Style** > **Advanced** and paste the code into **Head tag (HTML)**. See LumApps' [customization deployment guide](https://lumapps.github.io/customizations-api-doc/javascript/development-and-deployment.html). note Replace `{YOUR_GLEAN_DOMAIN}` in the code below with your company's Glean domain (e.g., app.glean.com, company.glean.com) * HTML Integration Code ``` ``` The integration code performs three key functions: 1. Disables LumApps' native search suggestions using CSS 2. Loads the Glean Web SDK from your company's domain 3. Attaches Glean's search functionality to the LumApps search box once the page has fully loaded info The script includes a polling mechanism to ensure the search box element is present before attempting to attach the Glean functionality, preventing any race conditions during page load. --- # React The following is an example usage of the inline components from within React. The imports used are custom, but should be self-explanatory in context. * GleanWebSDKGallery.tsx * styles/GleanWebSDKGalleryStyles.ts ``` import { useEffect, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; import injectScript from 'core/lib/injectScript'; import { QueryParam } from 'core/lib/QueryParam'; import { useLazyThunk } from 'core/redux/hooks'; import { selectBasePath } from 'core/redux/selectors/auth'; import { CreateAuthTokenResponse } from 'core/services/query_endpoint/models'; import { Theme } from 'embedded_search/Theme'; import { useQueryParam } from 'web/common/lib/hooks'; import { getWebAppUrl } from 'web/routing/lib/paths'; import styles from './styles/GleanWebSDKStyles'; const loginThunks = () => import('web/login/lib/thunks'); const light: Theme = { borderLight: '#eaebed', hover: '#f5fcfc', primaryHighlight: '#087d76', primaryHover: '#1ca99e', selected: '#e8fcfb', textPrimary: '#1b2126', textSecondary: '#71747d', visited: '#7400be', }; interface GleanWebSDKProps { authToken?: CreateAuthTokenResponse; } const GleanSearch = ({ authToken }: GleanWebSDKProps) => { const [query, setQuery] = useQueryParam(QueryParam.QUERY); const [datasource] = useQueryParam(QueryParam.TAB); const elementRef = useRef(null); const basePath = useSelector(selectBasePath); useEffect(() => { if (!elementRef.current) return; window.GleanWebSDK.renderSearchBox(elementRef.current, { authToken, backend: basePath, datasource, onSearch: setQuery, query, searchBoxCustomizations: { borderRadius: 10, boxShadow: 'none', horizontalMargin: 0, placeholderText: 'Search for anything (Beta)', verticalMargin: 0, }, theme: { light }, }); }, [authToken, basePath, datasource, query, setQuery]); return
; }; const GleanSearchResultsPage = ({ authToken }: GleanWebSDKProps) => { const [query, setQuery] = useQueryParam(QueryParam.QUERY); const [datasource, setDatasource] = useQueryParam(QueryParam.TAB); const elementRef = useRef(null); const basePath = useSelector(selectBasePath); useEffect(() => { if (!elementRef.current) return; window.GleanWebSDK.renderSearchResults(elementRef.current, { authToken, backend: basePath, datasource, onDatasourceChange: setDatasource, onSearch: setQuery, query, theme: { light }, }); }, [authToken, basePath, datasource, query, setDatasource, setQuery]); return
; }; const GleanWebSDKGallery = () => { const fetchAuthTokenThunk = useLazyThunk(loginThunks, 'fetchAuthToken'); const [authToken, setAuthToken] = useState< CreateAuthTokenResponse | undefined >(); const [isLoading, setIsLoading] = useState(true); useEffect(() => { (async () => { setAuthToken(await fetchAuthTokenThunk()); })(); injectScript({ id: 'embedded_search', src: getWebAppUrl('/embedded-search-latest.min.js'), }).then(() => setIsLoading(false)); }, [fetchAuthTokenThunk]); if (isLoading) return null; return (
); }; export default GleanWebSDKGallery; ``` ``` import * as Fonts from 'core/theme/Fonts.css'; import Stylesheet from 'core/types/Stylesheet'; export default Stylesheet({ container: { padding: 40, }, group: { marginBottom: 20, }, label: { ...Fonts.mediumHeader, }, }); ``` --- # Embedding Glean Search & Chat into SharePoint Sites ## Overview[​](#overview "Direct link to Overview") This guide provides step-by-step instructions for integrating Glean Search and Chat functionality into SharePoint sites using the SharePoint Framework (SPFx). ### Glean Search Embed Glean's search functionality directly into SharePoint pages ### Glean Chat Add Glean's conversational AI interface to SharePoint sites ## Prerequisites[​](#prerequisites "Direct link to Prerequisites") Before beginning the integration, ensure your SharePoint environment meets the following requirements: ### SharePoint Version Compatibility[​](#sharepoint-version-compatibility "Direct link to SharePoint Version Compatibility") The Glean sample web parts use SPFx 1.19 and require **SharePoint Online** (Microsoft 365). SharePoint Server on-premises releases support only older SPFx versions; see Microsoft's [SPFx compatibility reference](https://learn.microsoft.com/en-us/sharepoint/dev/spfx/compatibility). ### Required Permissions[​](#required-permissions "Direct link to Required Permissions") Uploading to the tenant App Catalog requires the **SharePoint Administrator** role or higher. Adding a deployed web part to a page requires permission to edit that page. ### App Catalog Verification[​](#app-catalog-verification "Direct link to App Catalog Verification") 1 Check App Catalog Availability Navigate to your App Catalog: `https://.sharepoint.com/sites/appcatalog/_layouts/15/tenantAppCatalog.aspx/manageApps` If you encounter a 404 error, the App Catalog needs to be created. 2 Create App Catalog (if needed) 1. Open the **SharePoint admin center**: `https://-admin.sharepoint.com` 2. Select **More features**, then under **Apps** select **Open** 3. Follow the prompts to create the App Catalog site See [Manage apps using the Apps site](https://learn.microsoft.com/en-us/sharepoint/use-app-catalog). 3 Verify Modern Pages Use modern SharePoint pages. The Glean samples attach to the modern SharePoint search box. ## Development Environment Setup[​](#development-environment-setup "Direct link to Development Environment Setup") 1 Install Node.js The Glean sample web parts use SPFx 1.19, which requires Node.js 18 (18.17.1 or later). Node.js 18 is past end of life, so use it only for building these samples. For other SPFx versions, check Microsoft's [SPFx compatibility reference](https://learn.microsoft.com/en-us/sharepoint/dev/spfx/compatibility). ``` # Check current version node -v # Install Node.js 18.x if needed # Visit https://nodejs.org/en/download/package-manager for installation instructions ``` 2 Install Development Tools The samples use the gulp-based SPFx toolchain. Install the gulp CLI globally: ``` npm install gulp-cli -g ``` 3 Set Up Code Editor Install Visual Studio Code or your preferred code editor for SPFx development. ## Creating the SPFx Web Part[​](#creating-the-spfx-web-part "Direct link to Creating the SPFx Web Part") ### Get the Sample Project[​](#get-the-sample-project "Direct link to Get the Sample Project") Start from a Glean sample instead of generating a new project. The current SPFx generator creates Heft-based projects, which do not use the gulp commands below. 1 Clone the Samples ``` git clone https://github.com/gleanwork/web-sdk-embed-examples.git cd web-sdk-embed-examples/sharepoint-webpart-search ``` For Chat, use `sharepoint-webpart-chat` instead. 2 Install Dependencies ``` npm install ``` ### Customize for Your Environment[​](#customize-for-your-environment "Direct link to Customize for Your Environment") 1 Update Search Element Selector The web part uses `document.querySelector('input[placeholder*="Search"]')` to locate SharePoint's search input. If your SharePoint site uses different search box styling or placeholder text, modify this selector in the web part code. 2 Configure Glean Settings Set these web part properties after you add the web part to a page: * **Backend URL** (`backendURL`) * **Application ID** (`applicationId`, Chat web part only) 3 Test in Development Test the web part in a non-production SharePoint environment before deploying to live sites. ## Build and Package[​](#build-and-package "Direct link to Build and Package") 1 Build the Project ``` gulp build ``` 2 Create Production Bundle ``` gulp bundle --ship ``` 3 Package the Solution ``` gulp package-solution --ship ``` This creates a `.sppkg` file in the `sharepoint/solution` folder. ## Deploy to SharePoint[​](#deploy-to-sharepoint "Direct link to Deploy to SharePoint") 1 Upload to App Catalog 1. Navigate to your SharePoint App Catalog 2. Upload the generated `.sppkg` file 3. Click **Deploy** when prompted 2 Enable App Choose one of these deployment options: * All Sites (Recommended) * Manual Addition Select **Enable this app and add it to all sites** to make the web part available across your tenant. Select **Only enable this app** to manually add the web part to specific sites. 3 Confirm Deployment Wait for the confirmation message indicating successful deployment. ## Add Web Part to SharePoint Pages[​](#add-web-part-to-sharepoint-pages "Direct link to Add Web Part to SharePoint Pages") 1 Navigate to Target Page Go to the SharePoint site where you want to add Glean functionality. 2 Edit the Page Click the **Edit** button to modify the page. 3 Add the Web Part 1. Click the **+** icon to add a new web part 2. Search for your Glean web part by name 3. Select and add it to the page note The web part can be added to any page section. It will activate when users interact with the SharePoint search box, regardless of where the web part is positioned. 4 Configure Properties Configure any web part properties specific to your Glean deployment. 5 Save and Publish Save and **Republish** the page to make the Glean functionality live. ## Testing the Integration[​](#testing-the-integration "Direct link to Testing the Integration") 1 Verify Search Functionality Test that Glean Search activates when users interact with the SharePoint search box. 2 Check API Connectivity Ensure that API calls to Glean services are functioning correctly. 3 Validate User Experience Confirm that the search interface and results display properly within the SharePoint environment. ## Chat Integration[​](#chat-integration "Direct link to Chat Integration") To implement Glean Chat instead of or in addition to Search, follow the same process using the Chat web part code. The main differences are: * Use the chat-specific web part code from [web-sdk-embed-examples/sharepoint-webpart-chat](https://github.com/gleanwork/web-sdk-embed-examples/tree/main/sharepoint-webpart-chat) * Configure the Application ID for your specific AI application * The chat web part renders Glean Chat in its own container on the page, so it does not need a search box selector ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") ### Common Issues[​](#common-issues "Direct link to Common Issues") **App Catalog Not Found (404 Error)** If you encounter a 404 error when accessing the App Catalog, it may not be created yet. Follow the App Catalog setup steps in the Prerequisites section. **Web Part Not Appearing** * Verify the web part is properly deployed to the App Catalog * Confirm the page is using the modern SharePoint experience * Check that you have sufficient permissions to add web parts **Search Box Not Detected** If the Glean functionality doesn't activate: * Inspect your SharePoint page to identify the correct search box selector * Update the `querySelector` in the web part code to match your page structure * Test the selector in the browser console before rebuilding **Glean script blocked due to Content Security Policy** Find your Glean Web app URL in the [admin console](https://app.glean.com/admin/about-glean). In the SharePoint admin center, go to **Advanced** > **Script sources** and add it as a trusted script source. See Microsoft's [trusted script sources guide](https://learn.microsoft.com/en-us/sharepoint/dev/spfx/content-securty-policy-trusted-script-sources). ### Getting Help[​](#getting-help "Direct link to Getting Help") For additional support with SharePoint integration: * Review the [SharePoint Framework documentation](https://learn.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-framework-overview) * Check the [SPFx Yeoman Generator guide](https://learn.microsoft.com/en-us/sharepoint/dev/spfx/yeoman-generator-for-spfx-intro) * Consult SharePoint admin resources for App Catalog management ## Technical Reference[​](#technical-reference "Direct link to Technical Reference") ### Code Structure[​](#code-structure "Direct link to Code Structure") The Glean web parts extend Microsoft's `BaseClientSideWebPart` class, which provides: * Lifecycle methods (`onInit`, `render`, `onDispose`) * Property management for user-defined settings * Context access for SharePoint resources ### Key Components[​](#key-components "Direct link to Key Components") * **Web Part Implementation**: Extends `BaseClientSideWebPart` with Glean-specific functionality * **Element Detection**: Uses `document.querySelector` to locate SharePoint search elements * **Dynamic Loading**: Loads the Glean Web SDK script with `SPComponentLoader.loadScript`. The samples load it from `app.glean.com`; replace this with your Glean app domain if it differs. * **Search Integration**: Calls the SDK's `attach` method on SharePoint's search input ### Useful Resources[​](#useful-resources "Direct link to Useful Resources") * [SharePoint Framework Overview](https://learn.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-framework-overview) * [SharePoint App Catalog Management](https://learn.microsoft.com/en-us/sharepoint/use-app-catalog) * [SPFx Development Environment Setup](https://learn.microsoft.com/en-us/sharepoint/dev/spfx/yeoman-generator-for-spfx-intro) --- # Zendesk ## Overview[​](#overview "Direct link to Overview") Glean offers two distinct options for implementing search functionality within Zendesk Guide: ### Modal Search A modal search interface that overlays the current page when activated ### Autocomplete & Results Page An integrated search experience with autocomplete and a dedicated results page note These implementation instructions are based on the [Copenhagen](https://support.zendesk.com/hc/en-us/articles/4408845834522) Zendesk Template. While they should work with other themes with minimal modifications, some adjustments may be needed depending on your specific theme. ## Modal Search Implementation[​](#modal-search-implementation "Direct link to Modal Search Implementation") 1 Access Theme Customization In **Knowledge admin**, click **Customize design** in the left navigation 2 Select Theme Locate the **Live theme** section and click **Customize** on your theme warning For live help centers, we recommend creating a copy of your theme for testing: 1. Find your live theme 2. Click the three vertical dots menu 3. Select **Copy** 3 Edit Theme Code 1. Click **Edit Code** to access the theme code editor ![Edit Code](/assets/images/edit-code-b9f3875f5d40e9d3e213b5afa631df38.png) 2. Open **script.js** from the left panel 3. Add the following code at the top level: ``` const gleanScript = document.createElement("script"); gleanScript.src = "https://{YOUR_GLEAN_DOMAIN}/embedded-search-latest.min.js"; gleanScript.defer = true; gleanScript.addEventListener("load", function () { GleanWebSDK.attach(document.querySelector("input[type=search]")); }); document.head.append(gleanScript); ``` 4. Click **Save** 4 Preview and Publish 1. Click **Preview** in the left panel to verify your changes 2. Once confirmed, apply the changes to your live theme 3. Click **Publish** to make the changes live ## Autocomplete and Search Results Implementation[​](#autocomplete-and-search-results-implementation "Direct link to Autocomplete and Search Results Implementation") 1 Import JavaScript Files 1. Access theme customization as described above 2. Click **Edit Code** 3. Open **document\_head.hbs** 4. Add the following script tag: ``` ``` ![Add Glean SDK](/assets/images/add-glean-sdk-b7b859e5768a18966f8d820fae90626a.png) 2 Replace Search UI Components ### Home Page Search[​](#home-page-search "Direct link to Home Page Search") 1. Open **home\_page.hbs** 2. Replace the existing `{{search}}` component with: ``` ``` ![Search Box Hero](/assets/images/search-box-hero-bae9d20a0e7ca23a69e5d741d0961402.png) ### Search Results Page[​](#search-results-page "Direct link to Search Results Page") 1. Open **search\_results.hbs** 2. Replace all existing code with: ```
``` ![Search Results](/assets/images/search-results-9867451e6a5e8c716730bbca2d8631a6.png) 3 Add Compact Search Box 1. Open **article\_page.hbs** 2. Replace the existing search code with: ```
``` ![Search Box Compact](/assets/images/search-box-compact-180bc329b45afd84650213cee1f49f3b.png) 4 Initialize Glean Search 1. Open **script.js** 2. Add the following code at the end of the file: ``` /** Setup Glean search **/ function initializeGleanSearch () { // Note: This should match the backend URL for your Glean setup const backend = ''; // See available customizations here: https://developers.glean.com/libraries/web-sdk/reference/interfaces/SearchBoxCustomizations const customizations = { boxShadow: 'none', borderRadius: 8, placeholderText: 'Search...', // Change me to a placeholder of your choice }; // Initialize GleanForZendesk GleanForZendeskGuide.init({ // This should match the search page used by your theme -- this usually matches /hc/search searchUrl: '/hc/search', // If your Glean setup supports guest users, mark this as true. // Note: If your Glean setup is internal users only, this option will be ignored. anonymous: false, backend, }).then((instance) => { // Attach hero search box instance.renderSearchBox('.glean-search-box', { searchBoxCustomizations: Object.assign({}, customizations, { fontSize: 20, // Additional customizations can be added here }), backend, }); // Attach compact search box instance.renderSearchBox('.glean-search-box--compact', { searchBoxCustomizations: Object.assign({}, customizations, { fontSize: 16, // Additional customizations can be added here }), backend, }); // Attach search results instance.renderSearchResults('.glean-search-results', { backend, showInlineSearchBox: true }); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeGleanSearch); } else { initializeGleanSearch(); } ``` 5 Verify and Publish 1. Click **Preview** to test your changes 2. Apply the changes to your live theme 3. Click **Publish** to make the changes live --- # Web SDK Web SDK # Embed Glean anywhere Drop permission-aware search, AI chat, and contextual recommendations into any internal surface. Every component is a one-line render call backed by your organization's Glean instance. Browse components[SDK reference](/libraries/web-sdk/reference/.md)`npm install @gleanwork/web-sdk` ## Six components, one SDK The previews below are illustrative, rendered with sample data from a fictional engineering portal — in your app, each component renders live against your organization's Glean instance. [ New chat Library AgentsTodayWho owns the payments…Canary alarm follow-up](/libraries/web-sdk/components/chat.md) [Who owns the payments service?Share](/libraries/web-sdk/components/chat.md) [The payments service is owned by the Payments Platform team.](/libraries/web-sdk/components/chat.md) [Priya Natarajan is the tech lead, and Marcus Webb is on call this week — the deploy and rollback runbook lives in the Engineering Wiki.](/libraries/web-sdk/components/chat.md) [Ask a follow-up…](/libraries/web-sdk/components/chat.md) [Glean ChatA full AI assistant grounded in your company knowledge, with citations back to the source.renderChat(element, options)](/libraries/web-sdk/components/chat.md) [payments](/libraries/web-sdk/components/autocomplete.md) [CPayments Service — Deploy & Rollback RunbookEngineering Wiki · Updated 2 days ago](/libraries/web-sdk/components/autocomplete.md) [Gpayments-servicegit.internal/payments-service · 1mo ago](/libraries/web-sdk/components/autocomplete.md) [from:teammate](/libraries/web-sdk/components/autocomplete.md) [type:bug, document, message, etc.](/libraries/web-sdk/components/autocomplete.md) [⏎ Enter Search](/libraries/web-sdk/components/autocomplete.md) [Search box + resultsCompose an autocomplete search box with a full results page for a custom search experience.renderSearchBox(element, options)](/libraries/web-sdk/components/autocomplete.md) [Sample CorpServicesRunbooksOn-call](/libraries/web-sdk/components/modal-search.md) [payments service runbook](/libraries/web-sdk/components/modal-search.md) [CPayments Service — Deploy & Rollback RunbookEngineering Wiki · Updated 2 days ago](/libraries/web-sdk/components/modal-search.md) [Gpayments-servicegit.internal/payments-service · 1mo ago](/libraries/web-sdk/components/modal-search.md) [from:teammate](/libraries/web-sdk/components/modal-search.md) [type:bug, document, message, etc.](/libraries/web-sdk/components/modal-search.md) [⏎ Enter Search](/libraries/web-sdk/components/modal-search.md) [powered by Glean](/libraries/web-sdk/components/modal-search.md) [Modal SearchAttach a complete search overlay to any input on your page — one method call.attach(inputElement, options)](/libraries/web-sdk/components/modal-search.md) [Search for anything at Glean](/libraries/web-sdk/components/recommendations.md) [Recommended for this page](/libraries/web-sdk/components/recommendations.md) [C](/libraries/web-sdk/components/recommendations.md) [Checkout — On-call RunbookUpdated 4 days ago](/libraries/web-sdk/components/recommendations.md) [J](/libraries/web-sdk/components/recommendations.md) [PAY-2114: Canary alarms during deployIn review](/libraries/web-sdk/components/recommendations.md) [P](/libraries/web-sdk/components/recommendations.md) [Priya Natarajan — Payments PlatformTech lead](/libraries/web-sdk/components/recommendations.md) [RecommendationsContextual suggestions for the current page, with a built-in search box.renderRecommendations(element, options)](/libraries/web-sdk/components/recommendations.md) [Search connectors](/libraries/web-sdk/components/settings.md) [Connected](/libraries/web-sdk/components/settings.md) [View and manage the connectors available.](/libraries/web-sdk/components/settings.md) [GGitHub](/libraries/web-sdk/components/settings.md) [JJira](/libraries/web-sdk/components/settings.md) [SSlackConnect](/libraries/web-sdk/components/settings.md) [CConfluenceConnect](/libraries/web-sdk/components/settings.md) [Glean SettingsBetaLet users connect personal datasources like GitHub and Slack without leaving your app.renderSettings(element, options)](/libraries/web-sdk/components/settings.md) [Sample CorpServicesRunbooksOn-call](/libraries/web-sdk/components/sidebar.md) [ChatSearch](/libraries/web-sdk/components/sidebar.md) [Ask Assistant anything](/libraries/web-sdk/components/sidebar.md) [Reference current page](/libraries/web-sdk/components/sidebar.md) [Ask Assistant anything…](/libraries/web-sdk/components/sidebar.md) [Sidebar SearchDeprecatedA slide-out Glean Assistant panel with chat and search.openSidebar(options)](/libraries/web-sdk/components/sidebar.md) Quickstart ## A search box in three steps 1. 1Add the SDK — install `@gleanwork/web-sdk` from npm, or load the script tag from your Glean web app domain. 2. 2Render a container element with `position: relative` and `display: block`. 3. 3Call `renderSearchBox` — users sign in with your existing SSO, and every result respects their permissions. See [Authentication](/libraries/web-sdk/authentication/overview.md) for token-based options. npmScript tag search.ts ``` import { renderSearchBox } from '@gleanwork/web-sdk' renderSearchBox(document.getElementById('search'), { backend: 'https://acme-be.glean.com/', onSearch: (query) => { location.assign('/search?q=' + encodeURIComponent(query)) }, }) ``` ## Works where your team works Step-by-step guides for the platforms teams embed Glean into most. [ReactWrap the SDK in components and hooks for your React app.](/libraries/web-sdk/guides/react.md)[SharePointShip Glean search inside SharePoint with an SPFx web part.](/libraries/web-sdk/guides/sharepoint.md)[ZendeskAdd Glean to your help center theme and agent workspace.](/libraries/web-sdk/guides/zendesk.md)[LumAppsEmbed Glean widgets in your LumApps intranet pages.](/libraries/web-sdk/guides/lumapps.md)[BrightspotIntegrate Glean search into Brightspot-powered sites.](/libraries/web-sdk/guides/brightspot.md) ## Authentication Widgets authenticate real users, so every search result and chat answer is scoped to what that person can already see. [SSO by defaultNo extra setup — users sign in with your existing identity provider the first time a widget loads.](/libraries/web-sdk/authentication/default-sso.md)[Server-to-server tokensMint short-lived user tokens on your backend and pass them to the SDK for a fully seamless session.](/libraries/web-sdk/authentication/server-to-server.md)[Third-party cookiesUnderstand how browser cookie policies affect embedded sessions, and how the SDK handles them.](/libraries/web-sdk/3rd-party-cookies.md) --- # Web SDK Reference ## Interfaces[​](#interfaces "Direct link to Interfaces") | Interface | Description | | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | [AuthTokenDetails](/libraries/web-sdk/reference/interfaces/AuthTokenDetails.md) | - | | [BoxCustomizations](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md) | Style and other options which can be customized on the embedded search widget box. | | [ChatCustomizations](/libraries/web-sdk/reference/interfaces/ChatCustomizations.md) | - | | [ChatHandle](/libraries/web-sdk/reference/interfaces/ChatHandle.md) | - | | [ChatOptions](/libraries/web-sdk/reference/interfaces/ChatOptions.md) | - | | [FilterConfig](/libraries/web-sdk/reference/interfaces/FilterConfig.md) | - | | [GleanWebSDK](/libraries/web-sdk/reference/interfaces/GleanWebSDK.md) | Entry point for all Glean embedded widgets. | | [GuestAuthProvider](/libraries/web-sdk/reference/interfaces/GuestAuthProvider.md) | Creates and manages auth tokens for guest users. Used primarily in Glean external search deployments | | [GuestAuthProviderOptions](/libraries/web-sdk/reference/interfaces/GuestAuthProviderOptions.md) | - | | [ModalSearchOptions](/libraries/web-sdk/reference/interfaces/ModalSearchOptions.md) | - | | [Options](/libraries/web-sdk/reference/interfaces/Options.md) | - | | [RecommendationsBoxCustomizations](/libraries/web-sdk/reference/interfaces/RecommendationsBoxCustomizations.md) | - | | [RecommendationsOptions](/libraries/web-sdk/reference/interfaces/RecommendationsOptions.md) | - | | [RecommendationsResultsOptions](/libraries/web-sdk/reference/interfaces/RecommendationsResultsOptions.md) | - | | [RecommendationsSearchOptions](/libraries/web-sdk/reference/interfaces/RecommendationsSearchOptions.md) | - | | [SearchBoxCustomizations](/libraries/web-sdk/reference/interfaces/SearchBoxCustomizations.md) | Style and other options which can be customized on the embedded search widget box. | | [SearchBoxOptions](/libraries/web-sdk/reference/interfaces/SearchBoxOptions.md) | - | | [SearchOptions](/libraries/web-sdk/reference/interfaces/SearchOptions.md) | - | | [SettingsOptions](/libraries/web-sdk/reference/interfaces/SettingsOptions.md) | - | | [TabbedSearchOptions](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md) | - | | [Theme](/libraries/web-sdk/reference/interfaces/Theme.md) | - | | [WidgetHandle](/libraries/web-sdk/reference/interfaces/WidgetHandle.md) | - | ## Type Aliases[​](#type-aliases "Direct link to Type Aliases") | Type Alias | Description | | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [ThemeVariant](/libraries/web-sdk/reference/type-aliases/ThemeVariant.md) | - | | [WebSdkChatEvent](/libraries/web-sdk/reference/type-aliases/WebSdkChatEvent.md) | Chat events that can be emitted by the chat component - chat:id\_update and chat:location\_update events may be sent multiple times for the same ID and the handler should be idempotent | ## Variables[​](#variables "Direct link to Variables") | Variable | Description | | -------------------------------------------------------------------------------------------- | ----------- | | [attach](/libraries/web-sdk/reference/variables/attach.md) | - | | [createGuestAuthProvider](/libraries/web-sdk/reference/variables/createGuestAuthProvider.md) | - | | [default](/libraries/web-sdk/reference/variables/default.md) | - | | [getChatOptionsFromUrl](/libraries/web-sdk/reference/variables/getChatOptionsFromUrl.md) | - | | [openSidebar](/libraries/web-sdk/reference/variables/openSidebar.md) | - | | [renderChat](/libraries/web-sdk/reference/variables/renderChat.md) | - | | [renderRecommendations](/libraries/web-sdk/reference/variables/renderRecommendations.md) | - | | [renderSearchBox](/libraries/web-sdk/reference/variables/renderSearchBox.md) | - | | [renderSearchResults](/libraries/web-sdk/reference/variables/renderSearchResults.md) | - | | [version](/libraries/web-sdk/reference/variables/version.md) | - | --- # AuthTokenDetails ## Export[​](#export "Direct link to Export") AuthTokenDetails ## Properties[​](#properties "Direct link to Properties") ### expirationTime[​](#expirationtime "Direct link to expirationTime") ``` expirationTime: number; ``` Unix timestamp for when this token expires (in seconds since epoch UTC). #### Memberof[​](#memberof "Direct link to Memberof") AuthTokenDetails *** ### token[​](#token "Direct link to token") ``` token: string; ``` An authentication token that can be passed to any endpoint via Bearer Authentication #### Memberof[​](#memberof-1 "Direct link to Memberof") AuthTokenDetails --- # BoxCustomizations Style and other options which can be customized on the embedded search widget box. ## Extended by[​](#extended-by "Direct link to Extended by") * [`SearchBoxCustomizations`](/libraries/web-sdk/reference/interfaces/SearchBoxCustomizations.md) ## Properties[​](#properties "Direct link to Properties") ### border?[​](#border "Direct link to border?") ``` optional border?: string; ``` The CSS border of the widget box *** ### borderRadius?[​](#borderradius "Direct link to borderRadius?") ``` optional borderRadius?: number; ``` The border radius of the widget box *** ### boxShadow?[​](#boxshadow "Direct link to boxShadow?") ``` optional boxShadow?: string; ``` The CSS box-shadow of the widget box *** ### horizontalMargin?[​](#horizontalmargin "Direct link to horizontalMargin?") ``` optional horizontalMargin?: number; ``` A CSS margin to the left and right of the widget box *** ### verticalMargin?[​](#verticalmargin "Direct link to verticalMargin?") ``` optional verticalMargin?: number; ``` A CSS margin above and below the widget box --- # ChatCustomizations ## Properties[​](#properties "Direct link to Properties") ### autocompletePosition?[​](#autocompleteposition "Direct link to autocompletePosition?") ``` optional autocompletePosition?: "above" | "below"; ``` Controls where the autocomplete dropdown appears relative to the chat input box. Only applies when `features.autocomplete` is enabled. * 'above' - Autocomplete results appear above the input box. * 'below' - Autocomplete results appear below the input box. Default is 'above'. *** ### container?[​](#container "Direct link to container?") ``` optional container?: BoxCustomizations; ``` Customizations for the widget box. *** ### features?[​](#features "Direct link to features?") ``` optional features?: object; ``` Options to control which chat features or UI elements are displayed in the widget. #### agentLibrary?[​](#agentlibrary "Direct link to agentLibrary?") ``` optional agentLibrary?: boolean; ``` The entry point to the agents library. Default is true. #### agentMetadata?[​](#agentmetadata "Direct link to agentMetadata?") ``` optional agentMetadata?: boolean; ``` The element that shows metadata about an embedded agent. Default is true. #### applicationLibrary?[​](#applicationlibrary "Direct link to applicationLibrary?") ``` optional applicationLibrary?: boolean; ``` The entry point to the AI apps library. Default is true. #### autocomplete?[​](#autocomplete "Direct link to autocomplete?") ``` optional autocomplete?: boolean; ``` Autocomplete suggestions shown in the chat input. Default is false. #### chatMenu?[​](#chatmenu "Direct link to chatMenu?") ``` optional chatMenu?: boolean; ``` The menu which displays chat history and other advanced features. Default is true. #### chatSettings?[​](#chatsettings "Direct link to chatSettings?") ``` optional chatSettings?: boolean; ``` The button that leads to the chat settings menu. Default is true. #### clearChat?[​](#clearchat "Direct link to clearChat?") ``` optional clearChat?: boolean; ``` The button that clears the current chat session. Default is true when chat menu is disabled. #### createPrompt?[​](#createprompt "Direct link to createPrompt?") ``` optional createPrompt?: boolean; ``` The button that opens prompt creation drawer. Default is true. #### feedback?[​](#feedback "Direct link to feedback?") ``` optional feedback?: boolean; ``` The button that opens the feedback form. Default is true. #### newChatButton?[​](#newchatbutton "Direct link to newChatButton?") ``` optional newChatButton?: boolean; ``` The ability to start a new chat from the chat menu. Disabling this will remove the new chat button in the chat menu. Default is true. #### promptLibrary?[​](#promptlibrary "Direct link to promptLibrary?") ``` optional promptLibrary?: boolean; ``` The entry point to the prompts library. Default is true. --- # ChatHandle ## Properties[​](#properties "Direct link to Properties") ### off[​](#off "Direct link to off") ``` off: (eventName, handler) => void; ``` #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | | ----------- | ------------------------------------------------------------------------------------------------ | | `eventName` | \| `"chat:page_view"` \| `"chat:id_update"` \| `"chat:location_update"` \| `"chat:action_click"` | | `handler` | (`event`) => `void` | #### Returns[​](#returns "Direct link to Returns") `void` *** ### on[​](#on "Direct link to on") ``` on: (eventName, handler) => void; ``` #### Parameters[​](#parameters-1 "Direct link to Parameters") | Parameter | Type | | ----------- | ------------------------------------------------------------------------------------------------ | | `eventName` | \| `"chat:page_view"` \| `"chat:id_update"` \| `"chat:location_update"` \| `"chat:action_click"` | | `handler` | (`event`) => `void` | #### Returns[​](#returns-1 "Direct link to Returns") `void` --- # ChatOptions ## Extends[​](#extends "Direct link to Extends") * [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) ## Properties[​](#properties "Direct link to Properties") ### agent?[​](#agent "Direct link to agent?") ``` optional agent?: "DEFAULT" | "GPT" | "FAST" | "ADVANCED"; ``` Specifies the agent to use for chat responses: * \[deprecating] DEFAULT - Integrates with your company's knowledge. This will soon be deprecated in favor of the FAST and ADVANCED `agent` values. * \[deprecating] GPT - Communicates directly with the LLM. This will soon be deprecated in favor of the FAST and ADVANCED `agent` values. * FAST - Uses an agent powered by the agentic engine that responds faster but may have lower quality results. Requires the agentic engine to be enabled in the deployment. * ADVANCED - Uses an agent powered by the agentic engine that thinks for longer and potentially makes more LLM calls to return higher quality results. Requires the agentic engine to be enabled in the deployment. *** ### agentId?[​](#agentid "Direct link to agentId?") ``` optional agentId?: string; ``` The ID of the Agent that should be used in chat. *** ### applicationId?[​](#applicationid "Direct link to applicationId?") ``` optional applicationId?: string; ``` The ID of the application used to determine the configuration of underlying chat processes. This should match the Application ID for an application created with the AI App builder. *** ### authMethod?[​](#authmethod "Direct link to authMethod?") ``` optional authMethod?: "sso" | "token"; ``` The authentication method to use in the embedded widget. sso - Logged out users will see a login button in the embedded widget. Clicking the button will open the SSO login flow in a new window. token - The embedding page obtains an auth token on behalf of the user and passes it to the embedded widget. If this method is specified the [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) and [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) options must also be specified. #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`authMethod`](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) *** ### authToken?[​](#authtoken "Direct link to authToken?") ``` optional authToken?: AuthTokenDetails; ``` An authentication token for the user. Providing this obviates the need for the user to separately login to Glean. May be obtained via: If an authToken is provided, [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) must also be provided. #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`authToken`](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) *** ### backend?[​](#backend "Direct link to backend?") ``` optional backend?: string; ``` Overrides the search backend server to use. Setting this explicitly is always recommended as it alleviates the user from from having to manually enter their email address in order to route to the backend instance to log into. It's also useful for routing users to a production vs staging server (if available). Example: `https://{your}-be.glean.com/` #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`backend`](/libraries/web-sdk/reference/interfaces/Options.md#backend) *** ### chatId?[​](#chatid "Direct link to chatId?") ``` optional chatId?: string; ``` An opaque id that stores the context of the current / new chat for the current session. *** ### customizations?[​](#customizations "Direct link to customizations?") ``` optional customizations?: ChatCustomizations; ``` Optional style customizations. *** ### disableAnalytics?[​](#disableanalytics "Direct link to disableAnalytics?") ``` optional disableAnalytics?: boolean; ``` If true, Glean will not collect client analytics or log activity for the embedded session. Analytics data is used to monitor and improve the product experience, so it's recommended to leave data collection on unless the user has specifically requested otherwise. #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`disableAnalytics`](/libraries/web-sdk/reference/interfaces/Options.md#disableanalytics) *** ### disableAssistant?[​](#disableassistant "Direct link to disableAssistant?") ``` optional disableAssistant?: boolean; ``` If true, Glean Assistant will not be enabled for the embedded session, including but not limited to generated answers, summaries, chat, and other features. #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`disableAssistant`](/libraries/web-sdk/reference/interfaces/Options.md#disableassistant) *** ### domainsToOpenInCurrentTab?[​](#domainstoopenincurrenttab "Direct link to domainsToOpenInCurrentTab?") ``` optional domainsToOpenInCurrentTab?: string[]; ``` Always open links from these domains in the current tab. Expects a list of one or more domain suffixes, e.g. `['example.com', 'foo.example.org']`. #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`domainsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/Options.md#domainstoopenincurrenttab) *** ### enable3PCookieAccessRequest?[​](#enable3pcookieaccessrequest "Direct link to enable3PCookieAccessRequest?") ``` optional enable3PCookieAccessRequest?: boolean; ``` Enable flow to request the user for access if third-party cookies are blocked. If true, the user will be prompted to allow cookie access for glean.com when they try to login. Default to true since api version >= 2025-06-19. Note: This flag will have no impact if third-party cookies are already allowed. Users will be able to login as usual. Refer to [Guide to third-party cookies in Glean](https://developers.glean.com/docs/browser_api/third_party_cookies/) for more details. #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`enable3PCookieAccessRequest`](/libraries/web-sdk/reference/interfaces/Options.md#enable3pcookieaccessrequest) *** ### enableActivityLogging?[​](#enableactivitylogging "Direct link to enableActivityLogging?") ``` optional enableActivityLogging?: boolean; ``` Logs an activity event for each URL change of the embedding page. This improves search quality when embedded within a datasource for which there are not other activity signals (e.g. Salesforce). Note: if disableAnalytics is true, activity logging will be disabled regardless of the value of this option. #### Inherited from[​](#inherited-from-7 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`enableActivityLogging`](/libraries/web-sdk/reference/interfaces/Options.md#enableactivitylogging) *** ### externalSessionId?[​](#externalsessionid "Direct link to externalSessionId?") ``` optional externalSessionId?: string; ``` An external session identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own session telemetry. #### Inherited from[​](#inherited-from-8 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`externalSessionId`](/libraries/web-sdk/reference/interfaces/Options.md#externalsessionid) *** ### externalUserId?[​](#externaluserid "Direct link to externalUserId?") ``` optional externalUserId?: string; ``` An external user identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own user telemetry. #### Inherited from[​](#inherited-from-9 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`externalUserId`](/libraries/web-sdk/reference/interfaces/Options.md#externaluserid) *** ### fontFaces?[​](#fontfaces "Direct link to fontFaces?") ``` optional fontFaces?: FontFaceDetails[]; ``` A list of custom font-faces to include in the embedded page. When provided, the custom font-faces can be selected via the fontFamily option. #### Inherited from[​](#inherited-from-10 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontFaces`](/libraries/web-sdk/reference/interfaces/Options.md#fontfaces) *** ### fontFamily?[​](#fontfamily "Direct link to fontFamily?") ``` optional fontFamily?: string; ``` The font-family used for text in the embedded widget. #### Inherited from[​](#inherited-from-11 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontFamily`](/libraries/web-sdk/reference/interfaces/Options.md#fontfamily) *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: "medium" | "large" | "larger"; ``` The base font size of the embedded app. Defaults to 'medium' which corresponds to the browser's default font size, typically 16px. The actual font sizes of elements are relative to this value, some will be larger or smaller. #### Inherited from[​](#inherited-from-12 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontSize`](/libraries/web-sdk/reference/interfaces/Options.md#fontsize) *** ### initialMessage?[​](#initialmessage "Direct link to initialMessage?") ``` optional initialMessage?: string; ``` Specifies the message sent by the user to initiate the chat. *** ### key?[​](#key "Direct link to key?") ``` optional key?: string; ``` Uniquely identify the widget. Must be used if more than one Glean widget of the same type are rendered on the page Allowed characters: \[a-z A-Z - \_ . ; :] #### Inherited from[​](#inherited-from-13 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`key`](/libraries/web-sdk/reference/interfaces/Options.md#key) *** ### landingPage?[​](#landingpage "Direct link to landingPage?") ``` optional landingPage?: "chat" | "agentLibrary"; ``` Specifies the landing page to show when the user first lands on the chat page. The default is 'chat'. *** ### locale?[​](#locale "Direct link to locale?") ``` optional locale?: string; ``` Overrides the default locale for the embedded widget users. If 'auto' (default) is used, Glean will automatically detect the locale from user's browser. If an unsupported locale is provided, Glean will fallback to use the closest supported locale. If user picked their locale for Glean, their choice will be respected. #### Inherited from[​](#inherited-from-14 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`locale`](/libraries/web-sdk/reference/interfaces/Options.md#locale) *** ### onAuthTokenRequired?[​](#onauthtokenrequired "Direct link to onAuthTokenRequired?") ``` optional onAuthTokenRequired?: | (() => Promise) | (() => void); ``` A callback invoked when the user requires a new authentication token. Only invoked if an [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is provided initially and is nearing expiration. This option is required when [Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'. **Signatures:** 1. `() => Promise` - (Recommended) Return a Promise that resolves to the new token details. The SDK will update the token internally without requiring a re-render. #### Example[​](#example "Direct link to Example") ``` onAuthTokenRequired: async () => { const token = await fetchNewToken() return { token: token.value, expirationTime: token.expiresAt } } 2. `() => void` - (Legacy) The callback must fetch a new token and call the SDK's render method with updated options containing the new token. ``` #### Inherited from[​](#inherited-from-15 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`onAuthTokenRequired`](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) *** ### promptId?[​](#promptid "Direct link to promptId?") ``` optional promptId?: string; ``` The ID of the prompt that will open initially in the chat page. This should match the ID for a prompt created in Glean. *** ### restrictToApplication?[​](#restricttoapplication "Direct link to restrictToApplication?") ``` optional restrictToApplication?: boolean; ``` If true, the chat will be restricted to the application specified by `applicationId` or the application associated with `chatId`. New Chat will only be available for the specified application. Chat history will be filtered to only show the specified application. *** ### source?[​](#source "Direct link to source?") ``` optional source?: string; ``` Specifies the source that initiated the chat. *** ### theme?[​](#theme "Direct link to theme?") ``` optional theme?: Partial>; ``` A theme applied throughout the embedded widget. If a theme variant (e.g. dark) isn't supplied, the default Glean theme will be used when the user has applied that variant. If any color is not supplied for a variant, the default Glean color will be used. #### Inherited from[​](#inherited-from-16 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`theme`](/libraries/web-sdk/reference/interfaces/Options.md#theme) *** ### themeVariant?[​](#themevariant "Direct link to themeVariant?") ``` optional themeVariant?: ThemeVariantOrAuto; ``` Overrides the theme variant for the embedded widget (default: light) If 'auto' is selected, Glean will automatically switch between light and dark mode and remain in sync with the user device. #### Inherited from[​](#inherited-from-17 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`themeVariant`](/libraries/web-sdk/reference/interfaces/Options.md#themevariant) *** ### unauthorizedMessage?[​](#unauthorizedmessage "Direct link to unauthorizedMessage?") ``` optional unauthorizedMessage?: string; ``` A message to display when the current session does not have valid authorization. The message only applies when [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is used ([Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'). It can be used to instruct the user on how to get back to an authorized session, e,g, "Please refresh the page" or "Sign in to \[SSO provider]". #### Inherited from[​](#inherited-from-18 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`unauthorizedMessage`](/libraries/web-sdk/reference/interfaces/Options.md#unauthorizedmessage) *** ### urlsToOpenInCurrentTab?[​](#urlstoopenincurrenttab "Direct link to urlsToOpenInCurrentTab?") ``` optional urlsToOpenInCurrentTab?: string[]; ``` Always open matching URLs in the current tab Should be an array of strings representing a regular expression. For example: "^https\://example\\.com.\*$" will match any URL that starts with ''. Rules: * Every regular expression MUST start with '^' to ensure it matches from the start of the URL. * Regular expressions do not include search params when matching the URL. * A maximum of 10 entries are allowed in the array. #### Inherited from[​](#inherited-from-19 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`urlsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/Options.md#urlstoopenincurrenttab) *** ### webAppUrl?[​](#webappurl "Direct link to webAppUrl?") ``` optional webAppUrl?: string; ``` The web page URL where users access Glean. For example: https\://\.glean.com. Admins can find this value at in the `Web app URL` field. #### Inherited from[​](#inherited-from-20 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`webAppUrl`](/libraries/web-sdk/reference/interfaces/Options.md#webappurl) --- # FilterConfig ## Properties[​](#properties "Direct link to Properties") ### iconName[​](#iconname "Direct link to iconName") ``` iconName: string; ``` The name of a glyph icon to display next to the filter name. Supported values are a subset of those found at . Contact Glean to add support for new values. *** ### key[​](#key "Direct link to key") ``` key: string; ``` The unique identifier of the filter. If the filter is based on a property of custom datasource documents, the key should match the name of the custom property. *** ### placeholder[​](#placeholder "Direct link to placeholder") ``` placeholder: string; ``` Placeholder text to show when no filter value is selected. --- # GleanWebSDK Entry point for all Glean embedded widgets. The SDK will dispatch a `glean:ready` event when the GleanWebSDK is ready to use, see below for examples of how to use the event. Note that you only need this if you are integrating Glean via a deferred script tag. ## Example[​](#example "Direct link to Example") ``` // example via plain JS let isGleanReady = !!window.GleanWebSDK window.addEventListener("glean:ready", () => { isGleanReady = true }) if (isGleanReady) { // ready to use window.GleanWebSDK. } // example via react hook const subscribe = (callback: () => void) => { window.addEventListener("glean:ready", callback) return window.removeEventListener("glean:ready", callback) } const useIsGleanReady = useSyncExternalStore(subscribe, () => !!window.GleanWebSDK) const YourComponent = () => { const isGleanReady = useIsGleanReady() useEffect(() => { if (isGleanReady) { // ready to use window.GleanWebSDK. } }, [isGleanReady]) } ``` ## Properties[​](#properties "Direct link to Properties") ### attach[​](#attach "Direct link to attach") ``` attach: (element, options?) => WidgetHandle; ``` Attaches a complete, modal Glean Search to the given input element. #### User interface[​](#user-interface "Direct link to User interface") When the given input element is clicked, a modal dialog is immediately presented over the center of the page. It displays a more prominent search box and initial suggested documents and queries. As text is entered, autocomplete is performed, refining the suggestions. Upon pressing enter, the full search results are displayed in the modal. The modal is dismissed by clicking outside of its boundaries or on the close button. External links are opened in a new tab (`target=_blank`). If a non-empty query is passed in options the search modal will automatically open and display results for the query. #### Usage[​](#usage "Direct link to Usage") 1. Render a dummy search input box to the page with the desired styling. 2. Pass a reference to it to this method along with appropriate options. This method will handle all user interaction. 3. Handle the [SearchOptions.onSearch](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) callback if desired (e.g. for tracking purposes). #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | ---------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | `element` | `HTMLElement` | A focusable element such as a text `input` or `contenteditable`. | | `options?` | [`ModalSearchOptions`](/libraries/web-sdk/reference/interfaces/ModalSearchOptions.md) | Configuration options for the search modal. | #### Returns[​](#returns "Direct link to Returns") [`WidgetHandle`](/libraries/web-sdk/reference/interfaces/WidgetHandle.md) An imperative handle ([WidgetHandle](/libraries/web-sdk/reference/interfaces/WidgetHandle.md)) for Glean modal search. *** ### ~~attachAutocomplete~~[​](#attachautocomplete "Direct link to attachautocomplete") ``` attachAutocomplete: (element, options) => WidgetHandle; ``` #### Parameters[​](#parameters-1 "Direct link to Parameters") | Parameter | Type | | --------- | --------------------------------------------------------------------------------- | | `element` | `HTMLElement` | | `options` | [`SearchBoxOptions`](/libraries/web-sdk/reference/interfaces/SearchBoxOptions.md) | #### Returns[​](#returns-1 "Direct link to Returns") [`WidgetHandle`](/libraries/web-sdk/reference/interfaces/WidgetHandle.md) #### Deprecated[​](#deprecated "Direct link to Deprecated") Renamed `renderSearchBox`. *** ### createGuestAuthProvider[​](#createguestauthprovider "Direct link to createGuestAuthProvider") ``` createGuestAuthProvider: (options) => GuestAuthProvider; ``` Creates an auth provider to generate auth tokens for guest users. This is only supported if you are using Glean External Search. #### Parameters[​](#parameters-2 "Direct link to Parameters") | Parameter | Type | | --------- | ------------------------------------------------------------------------------------------------- | | `options` | [`GuestAuthProviderOptions`](/libraries/web-sdk/reference/interfaces/GuestAuthProviderOptions.md) | #### Returns[​](#returns-2 "Direct link to Returns") [`GuestAuthProvider`](/libraries/web-sdk/reference/interfaces/GuestAuthProvider.md) *** ### getChatOptionsFromUrl[​](#getchatoptionsfromurl "Direct link to getChatOptionsFromUrl") ``` getChatOptionsFromUrl: (deleteParams) => ChatUrlParams; ``` Attempts to read some configuration options for the embedded chat widget from the current URL of the embedding page. See [ChatOptions](/libraries/web-sdk/reference/interfaces/ChatOptions.md) for details. #### Usage[​](#usage-1 "Direct link to Usage") renderChat(containerRef.current, { ...getChatOptionsFromUrl(), chatId: searchParams.get("chatId") ?? "", onChat: (chatId: string) => setSearchParams({ chatId }), onSearch: (query: string) => navigate({ pathname: '/search', search: new URLSearchParams({ query }).toString() }), }); #### Parameters[​](#parameters-3 "Direct link to Parameters") | Parameter | Type | | -------------- | --------- | | `deleteParams` | `boolean` | #### Returns[​](#returns-3 "Direct link to Returns") `ChatUrlParams` *** ### ~~openSidebar~~[​](#opensidebar "Direct link to opensidebar") ``` openSidebar: (options) => Promise; ``` Opens a sidebar with a search box, recommendations and results. #### User interface[​](#user-interface-1 "Direct link to User interface") A sidebar with a search box slides in from the right of the page. If [SearchOptions.query](/libraries/web-sdk/reference/interfaces/SearchOptions.md#query) is given then search results are displayed for the given query by default. When a search result is clicked, it is opened in a new tab (`target=_blank`). The sidebar is closed when the close button within the sidebar is clicked or the escape key is pressed. #### Parameters[​](#parameters-4 "Direct link to Parameters") | Parameter | Type | Description | | --------- | --------------------------------------------------------------------------- | --------------------------------------------- | | `options` | [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md) | Configuration options for the search results. | #### Returns[​](#returns-4 "Direct link to Returns") `Promise`<`void`> #### Deprecated[​](#deprecated-1 "Direct link to Deprecated") will be removed in future versions. *** ### renderChat[​](#renderchat "Direct link to renderChat") ``` renderChat: (element, options) => ChatHandle; ``` Renders Glean AI chat inside the given container element #### Usage[​](#usage-2 "Direct link to Usage") 1. Render a block level element in the page with the desired dimensions in the viewport. 2. Pass it to this method along with appropriate options. #### Parameters[​](#parameters-5 "Direct link to Parameters") | Parameter | Type | Description | | --------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `element` | `HTMLElement` | A containing HTML element such as a `div` to render the widget into. The container must be a block-level element with a non static position such as 'relative'. | | `options` | [`ChatOptions`](/libraries/web-sdk/reference/interfaces/ChatOptions.md) | Configuration options for the chat widget | #### Returns[​](#returns-5 "Direct link to Returns") [`ChatHandle`](/libraries/web-sdk/reference/interfaces/ChatHandle.md) An imperative handle ([ChatHandle](/libraries/web-sdk/reference/interfaces/ChatHandle.md)) for Glean chat. *** ### renderRecommendations[​](#renderrecommendations "Direct link to renderRecommendations") ``` renderRecommendations: (element, options) => void; ``` Renders Glean recommendations inside the given container element #### User Interface[​](#user-interface-2 "Direct link to User Interface") The widget includes a Glean search box and shows contextual recommendations based on the current page. On clicking the search box, autocomplete dropdown opens with suggestions based on the query being typed in. This may extend beyond the container bounds based on the suggestions. Upon pressing enter, the search results are displayed responsively in the container along with datasources tabs and facet filters. Search results, when clicked are opened in a new tab. On the search page, a back button is shown beside the search box which can be used to navigate to the previous page. #### Usage[​](#usage-3 "Direct link to Usage") 1. Render a block level element in the page with the desired dimensions in the viewport. Minimum height recommended is 600px. 2. Pass it to this method along with appropriate options. #### Parameters[​](#parameters-6 "Direct link to Parameters") | Parameter | Type | Description | | --------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `element` | `HTMLElement` | A containing HTML element such as a `div` to render the recommendations widget into. The container must be a block-level element with a non static position such as 'relative'. | | `options` | [`RecommendationsOptions`](/libraries/web-sdk/reference/interfaces/RecommendationsOptions.md) | Configuration options for the recommendations widget | #### Returns[​](#returns-6 "Direct link to Returns") `void` *** ### renderSearchBox[​](#rendersearchbox "Direct link to renderSearchBox") ``` renderSearchBox: (element, options) => WidgetHandle; ``` Displays Glean Search autocomplete inside the given container element. #### User interface[​](#user-interface-3 "Direct link to User interface") A search box is rendered within the bounds of the provided container element. The box will take up the full space of the container minus any space required for the margins specified in the customization options [SearchBoxOptions.searchBoxCustomizations](/libraries/web-sdk/reference/interfaces/SearchBoxOptions.md#searchboxcustomizations). For example, if the search box should be 40px tall with a 5px margin on the top and bottom, the container should be 50px tall with `verticalMargin: 5`. If a `boxShadow` is specified, the margin should be large enough to display it. #### Usage[​](#usage-4 "Direct link to Usage") 1. Render a container element to the page with the desired size. 2. Pass it to this method along with appropriate options. 3. Handle the [SearchBoxOptions.onSearch](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) callback as desired (e.g. by calling [GleanWebSDK.renderSearchResults](#rendersearchresults)) and updating the page title/url. #### Parameters[​](#parameters-7 "Direct link to Parameters") | Parameter | Type | Description | | --------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `element` | `HTMLElement` | A containing HTML element such as a `div` to render the search box into. The container must have both `position:relative` and `display:block` applied. | | `options` | [`SearchBoxOptions`](/libraries/web-sdk/reference/interfaces/SearchBoxOptions.md) | Configuration options for the search box. | #### Returns[​](#returns-7 "Direct link to Returns") [`WidgetHandle`](/libraries/web-sdk/reference/interfaces/WidgetHandle.md) An imperative handle ([WidgetHandle](/libraries/web-sdk/reference/interfaces/WidgetHandle.md)) for Glean autocomplete. *** ### renderSearchResults[​](#rendersearchresults "Direct link to renderSearchResults") ``` renderSearchResults: (element, options) => void; ``` Renders Glean Search results into the given element. #### User interface[​](#user-interface-4 "Direct link to User interface") The search results, datasource tabs and facet filters are displayed responsively to fill the entire element. The caller is responsible for rendering a search input box and handling callback events. When a link that results in a new query is clicked (such as a spell correction), the [TabbedSearchOptions.onSearch](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) callback is invoked but the query is not automatically changed. When the user changes datasource tabs, the interface is updated and the [TabbedSearchOptions.onDatasourceChange](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md#ondatasourcechange) callback is invoked. When a search result is clicked, it is opened in a new tab (`target=_blank`). #### Usage[​](#usage-5 "Direct link to Usage") 1. Render a block level element in the page with the desired dimensions in the viewport. 2. Pass it to this method along with appropriate options. 3. Respond to the [TabbedSearchOptions.onSearch](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) callback by invoking this render method with the same element and the new query. At the same time, it's usually appropriate to update any search box text, page title and URL. 4. Optionally, respond to the [TabbedSearchOptions.onDatasourceChange](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md#ondatasourcechange) callback by updating the URL to preserve state. #### Parameters[​](#parameters-8 "Direct link to Parameters") | Parameter | Type | Description | | --------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `element` | `HTMLElement` | A containing HTML element such as a `div` to render the search box into. The container must have both `position:relative` and `display:block` applied. | | `options` | [`TabbedSearchOptions`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md) | Configuration options for the search results. | #### Returns[​](#returns-8 "Direct link to Returns") `void` *** ### renderSettings[​](#rendersettings "Direct link to renderSettings") ``` renderSettings: (element, options) => void; ``` **`Beta`** Renders Glean settings inside the given container element #### Usage[​](#usage-6 "Direct link to Usage") 1. Render a block level element in the page with the desired dimensions in the viewport. It is recommended that the width be at least 800px and the height be at least 600px. 2. Pass it to this method along with appropriate options. #### Parameters[​](#parameters-9 "Direct link to Parameters") | Parameter | Type | Description | | --------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `element` | `HTMLElement` | A containing HTML element such as a `div` to render the widget into. The container must be a block-level element with a non static position such as 'relative'. | | `options` | [`SettingsOptions`](/libraries/web-sdk/reference/interfaces/SettingsOptions.md) | Configuration options for the settings widget This API is experimental and may change at any time. | #### Returns[​](#returns-9 "Direct link to Returns") `void` --- # Abstract Interface: GuestAuthProvider Creates and manages auth tokens for guest users. Used primarily in Glean external search deployments ## Properties[​](#properties "Direct link to Properties") ### createAuthToken[​](#createauthtoken "Direct link to createAuthToken") ``` createAuthToken: () => Promise; ``` Creates a new guest auth token and returns the same #### Returns[​](#returns "Direct link to Returns") `Promise`<[`AuthTokenDetails`](/libraries/web-sdk/reference/interfaces/AuthTokenDetails.md)> *** ### getAuthToken[​](#getauthtoken "Direct link to getAuthToken") ``` getAuthToken: () => Promise; ``` Retrieves a stored guest auth token if it exists. If no token exists, a new guest auth token is created. #### Returns[​](#returns-1 "Direct link to Returns") `Promise`<[`AuthTokenDetails`](/libraries/web-sdk/reference/interfaces/AuthTokenDetails.md)> --- # GuestAuthProviderOptions ## Properties[​](#properties "Direct link to Properties") ### backend[​](#backend "Direct link to backend") ``` backend: string; ``` The search backend server to use. Should match the backend URL for your Glean external search setup. Example: `https://{your}-be.glean.com/` --- # ModalSearchOptions ## Extends[​](#extends "Direct link to Extends") * [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md) ## Properties[​](#properties "Direct link to Properties") ### authMethod?[​](#authmethod "Direct link to authMethod?") ``` optional authMethod?: "sso" | "token"; ``` The authentication method to use in the embedded widget. sso - Logged out users will see a login button in the embedded widget. Clicking the button will open the SSO login flow in a new window. token - The embedding page obtains an auth token on behalf of the user and passes it to the embedded widget. If this method is specified the [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) and [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) options must also be specified. #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`authMethod`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#authmethod) *** ### authToken?[​](#authtoken "Direct link to authToken?") ``` optional authToken?: AuthTokenDetails; ``` An authentication token for the user. Providing this obviates the need for the user to separately login to Glean. May be obtained via: If an authToken is provided, [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) must also be provided. #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`authToken`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#authtoken) *** ### backend?[​](#backend "Direct link to backend?") ``` optional backend?: string; ``` Overrides the search backend server to use. Setting this explicitly is always recommended as it alleviates the user from from having to manually enter their email address in order to route to the backend instance to log into. It's also useful for routing users to a production vs staging server (if available). Example: `https://{your}-be.glean.com/` #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`backend`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#backend) *** ### datasource?[​](#datasource "Direct link to datasource?") ``` optional datasource?: string; ``` Select the search tab for the given datasource (e.g. `'confluence'`, `'gdrive'`, `'people'`) instead of the all tab. Other datasource tabs will be shown if results are available for the current query. See also [SearchOptions.datasourcesFilter](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter). #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasource`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource) *** ### datasourcesFilter?[​](#datasourcesfilter "Direct link to datasourcesFilter?") ``` optional datasourcesFilter?: string[]; ``` Filter search results to only the given list of datasources (e.g. `['figma']`, `['jira', 'zendesk']`). Results from other datasources will not be available. See also [SearchOptions.datasource](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource). #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasourcesFilter`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter) *** ### disableAnalytics?[​](#disableanalytics "Direct link to disableAnalytics?") ``` optional disableAnalytics?: boolean; ``` If true, Glean will not collect client analytics or log activity for the embedded session. Analytics data is used to monitor and improve the product experience, so it's recommended to leave data collection on unless the user has specifically requested otherwise. #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`disableAnalytics`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#disableanalytics) *** ### disableAssistant?[​](#disableassistant "Direct link to disableAssistant?") ``` optional disableAssistant?: boolean; ``` If true, Glean Assistant will not be enabled for the embedded session, including but not limited to generated answers, summaries, chat, and other features. #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`disableAssistant`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#disableassistant) *** ### domainsToOpenInCurrentTab?[​](#domainstoopenincurrenttab "Direct link to domainsToOpenInCurrentTab?") ``` optional domainsToOpenInCurrentTab?: string[]; ``` Always open links from these domains in the current tab. Expects a list of one or more domain suffixes, e.g. `['example.com', 'foo.example.org']`. #### Inherited from[​](#inherited-from-7 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`domainsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#domainstoopenincurrenttab) *** ### enable3PCookieAccessRequest?[​](#enable3pcookieaccessrequest "Direct link to enable3PCookieAccessRequest?") ``` optional enable3PCookieAccessRequest?: boolean; ``` Enable flow to request the user for access if third-party cookies are blocked. If true, the user will be prompted to allow cookie access for glean.com when they try to login. Default to true since api version >= 2025-06-19. Note: This flag will have no impact if third-party cookies are already allowed. Users will be able to login as usual. Refer to [Guide to third-party cookies in Glean](https://developers.glean.com/docs/browser_api/third_party_cookies/) for more details. #### Inherited from[​](#inherited-from-8 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`enable3PCookieAccessRequest`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#enable3pcookieaccessrequest) *** ### enableActivityLogging?[​](#enableactivitylogging "Direct link to enableActivityLogging?") ``` optional enableActivityLogging?: boolean; ``` Logs an activity event for each URL change of the embedding page. This improves search quality when embedded within a datasource for which there are not other activity signals (e.g. Salesforce). Note: if disableAnalytics is true, activity logging will be disabled regardless of the value of this option. #### Inherited from[​](#inherited-from-9 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`enableActivityLogging`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#enableactivitylogging) *** ### externalSessionId?[​](#externalsessionid "Direct link to externalSessionId?") ``` optional externalSessionId?: string; ``` An external session identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own session telemetry. #### Inherited from[​](#inherited-from-10 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`externalSessionId`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#externalsessionid) *** ### externalUserId?[​](#externaluserid "Direct link to externalUserId?") ``` optional externalUserId?: string; ``` An external user identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own user telemetry. #### Inherited from[​](#inherited-from-11 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`externalUserId`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#externaluserid) *** ### filters?[​](#filters "Direct link to filters?") ``` optional filters?: FilterValue[]; ``` Filters applied on all searches done by the user. Any filters in this list will override any manually input filters from users or filters defined in [initialFilters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) that share the same key. These filters are non-modifiable by the user, and will not show up in the UI as being applied. For example, if a filter with some key `keyA` is specified here, then: * Users will not be able to modify any filters corresponding to `keyA` in the UI in any way * The filter dropdown for that key will not show up on the UI (nor in the overflow button) * Search bar operators (i.e. `keyA:[value]`) with that key will be overridden and removed from the search bar query * Any filters defined in [initialFilters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) with `keyA` will be ignored. When specifying the list of filters the same `key` may be specified more than once, in which case the `value`s for that key are combined via an 'OR' operation. The `value`s for different `key`s are combined via 'AND'. For example, this set of filters will match all results that were updated in the past week AND are of type "bug" OR "task": ``` [{ key: "type", value: "bug" }, { key: "type", value: "task" }, { key: "updated", value: "past_week" }] ``` #### Inherited from[​](#inherited-from-12 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`filters`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#filters) *** ### fontFaces?[​](#fontfaces "Direct link to fontFaces?") ``` optional fontFaces?: FontFaceDetails[]; ``` A list of custom font-faces to include in the embedded page. When provided, the custom font-faces can be selected via the fontFamily option. #### Inherited from[​](#inherited-from-13 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontFaces`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontfaces) *** ### fontFamily?[​](#fontfamily "Direct link to fontFamily?") ``` optional fontFamily?: string; ``` The font-family used for text in the embedded widget. #### Inherited from[​](#inherited-from-14 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontFamily`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontfamily) *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: "medium" | "large" | "larger"; ``` The base font size of the embedded app. Defaults to 'medium' which corresponds to the browser's default font size, typically 16px. The actual font sizes of elements are relative to this value, some will be larger or smaller. #### Inherited from[​](#inherited-from-15 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontSize`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontsize) *** ### hideAutocomplete?[​](#hideautocomplete "Direct link to hideAutocomplete?") ``` optional hideAutocomplete?: boolean; ``` If true, search boxes will not render the autocomplete dropdown. #### Inherited from[​](#inherited-from-16 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`hideAutocomplete`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#hideautocomplete) *** ### ~~initialFilters?~~[​](#initialfilters "Direct link to initialfilters") ``` optional initialFilters?: FilterValue[]; ``` #### Deprecated[​](#deprecated "Direct link to Deprecated") Use [filters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#filters) instead. #### Inherited from[​](#inherited-from-17 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`initialFilters`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) *** ### key?[​](#key "Direct link to key?") ``` optional key?: string; ``` Uniquely identify the widget. Must be used if more than one Glean widget of the same type are rendered on the page Allowed characters: \[a-z A-Z - \_ . ; :] #### Inherited from[​](#inherited-from-18 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`key`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#key) *** ### locale?[​](#locale "Direct link to locale?") ``` optional locale?: string; ``` Overrides the default locale for the embedded widget users. If 'auto' (default) is used, Glean will automatically detect the locale from user's browser. If an unsupported locale is provided, Glean will fallback to use the closest supported locale. If user picked their locale for Glean, their choice will be respected. #### Inherited from[​](#inherited-from-19 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`locale`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#locale) *** ### onAuthTokenRequired?[​](#onauthtokenrequired "Direct link to onAuthTokenRequired?") ``` optional onAuthTokenRequired?: | (() => Promise) | (() => void); ``` A callback invoked when the user requires a new authentication token. Only invoked if an [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is provided initially and is nearing expiration. This option is required when [Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'. **Signatures:** 1. `() => Promise` - (Recommended) Return a Promise that resolves to the new token details. The SDK will update the token internally without requiring a re-render. #### Example[​](#example "Direct link to Example") ``` onAuthTokenRequired: async () => { const token = await fetchNewToken() return { token: token.value, expirationTime: token.expiresAt } } 2. `() => void` - (Legacy) The callback must fetch a new token and call the SDK's render method with updated options containing the new token. ``` #### Inherited from[​](#inherited-from-20 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onAuthTokenRequired`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onauthtokenrequired) *** ### onChat?[​](#onchat "Direct link to onChat?") ``` optional onChat?: (chatId?) => void; ``` A callback invoked when the user tries to initiate a chat from search pages, like follow ups from AI Answer. If provided, we will allow and show follow ups on AI Answer for user to continue to chat from there, typical usage can be: 1. open the chat in a new page 2. or open chat with `renderChat` method on the same page #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | -------- | -------------------------------------------------------------------------------------- | | `chatId?` | `string` | An opaque id that stores the context of the current / new chat for the current session | #### Returns[​](#returns "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-21 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onChat`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onchat) *** ### onDetach?[​](#ondetach "Direct link to onDetach?") ``` optional onDetach?: () => void; ``` Listener invoked when the frame is detached and focus is reverted to the native search box. #### Returns[​](#returns-1 "Direct link to Returns") `void` *** ### onSearch[​](#onsearch "Direct link to onSearch") ``` onSearch: (query) => void; ``` A callback invoked when the user performs a search query. #### Parameters[​](#parameters-1 "Direct link to Parameters") | Parameter | Type | | --------- | -------- | | `query` | `string` | #### Returns[​](#returns-2 "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-22 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onSearch`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) *** ### query?[​](#query "Direct link to query?") ``` optional query?: string; ``` The initial query text to perform/display. #### Inherited from[​](#inherited-from-23 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`query`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#query) *** ### searchBoxCustomizations?[​](#searchboxcustomizations "Direct link to searchBoxCustomizations?") ``` optional searchBoxCustomizations?: SearchBoxCustomizations; ``` Optional style customizations for the search box. *** ### showNativeSearchToggle?[​](#shownativesearchtoggle "Direct link to showNativeSearchToggle?") ``` optional showNativeSearchToggle?: boolean; ``` Specifies whether the user should see an option allowing them to revert back to the native search experience. *** ### theme?[​](#theme "Direct link to theme?") ``` optional theme?: Partial>; ``` A theme applied throughout the embedded widget. If a theme variant (e.g. dark) isn't supplied, the default Glean theme will be used when the user has applied that variant. If any color is not supplied for a variant, the default Glean color will be used. #### Inherited from[​](#inherited-from-24 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`theme`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#theme) *** ### themeVariant?[​](#themevariant "Direct link to themeVariant?") ``` optional themeVariant?: ThemeVariantOrAuto; ``` Overrides the theme variant for the embedded widget (default: light) If 'auto' is selected, Glean will automatically switch between light and dark mode and remain in sync with the user device. #### Inherited from[​](#inherited-from-25 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`themeVariant`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#themevariant) *** ### unauthorizedMessage?[​](#unauthorizedmessage "Direct link to unauthorizedMessage?") ``` optional unauthorizedMessage?: string; ``` A message to display when the current session does not have valid authorization. The message only applies when [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is used ([Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'). It can be used to instruct the user on how to get back to an authorized session, e,g, "Please refresh the page" or "Sign in to \[SSO provider]". #### Inherited from[​](#inherited-from-26 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`unauthorizedMessage`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#unauthorizedmessage) *** ### urlsToOpenInCurrentTab?[​](#urlstoopenincurrenttab "Direct link to urlsToOpenInCurrentTab?") ``` optional urlsToOpenInCurrentTab?: string[]; ``` Always open matching URLs in the current tab Should be an array of strings representing a regular expression. For example: "^https\://example\\.com.\*$" will match any URL that starts with ''. Rules: * Every regular expression MUST start with '^' to ensure it matches from the start of the URL. * Regular expressions do not include search params when matching the URL. * A maximum of 10 entries are allowed in the array. #### Inherited from[​](#inherited-from-27 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`urlsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#urlstoopenincurrenttab) *** ### webAppUrl?[​](#webappurl "Direct link to webAppUrl?") ``` optional webAppUrl?: string; ``` The web page URL where users access Glean. For example: https\://\.glean.com. Admins can find this value at in the `Web app URL` field. #### Inherited from[​](#inherited-from-28 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`webAppUrl`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#webappurl) --- # Options ## Extended by[​](#extended-by "Direct link to Extended by") * [`ChatOptions`](/libraries/web-sdk/reference/interfaces/ChatOptions.md) * [`RecommendationsOptions`](/libraries/web-sdk/reference/interfaces/RecommendationsOptions.md) * [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md) * [`SettingsOptions`](/libraries/web-sdk/reference/interfaces/SettingsOptions.md) ## Properties[​](#properties "Direct link to Properties") ### authMethod?[​](#authmethod "Direct link to authMethod?") ``` optional authMethod?: "sso" | "token"; ``` The authentication method to use in the embedded widget. sso - Logged out users will see a login button in the embedded widget. Clicking the button will open the SSO login flow in a new window. token - The embedding page obtains an auth token on behalf of the user and passes it to the embedded widget. If this method is specified the [Options.authToken](#authtoken) and [Options.onAuthTokenRequired](#onauthtokenrequired) options must also be specified. *** ### authToken?[​](#authtoken "Direct link to authToken?") ``` optional authToken?: AuthTokenDetails; ``` An authentication token for the user. Providing this obviates the need for the user to separately login to Glean. May be obtained via: If an authToken is provided, [Options.onAuthTokenRequired](#onauthtokenrequired) must also be provided. *** ### backend?[​](#backend "Direct link to backend?") ``` optional backend?: string; ``` Overrides the search backend server to use. Setting this explicitly is always recommended as it alleviates the user from from having to manually enter their email address in order to route to the backend instance to log into. It's also useful for routing users to a production vs staging server (if available). Example: `https://{your}-be.glean.com/` *** ### disableAnalytics?[​](#disableanalytics "Direct link to disableAnalytics?") ``` optional disableAnalytics?: boolean; ``` If true, Glean will not collect client analytics or log activity for the embedded session. Analytics data is used to monitor and improve the product experience, so it's recommended to leave data collection on unless the user has specifically requested otherwise. *** ### disableAssistant?[​](#disableassistant "Direct link to disableAssistant?") ``` optional disableAssistant?: boolean; ``` If true, Glean Assistant will not be enabled for the embedded session, including but not limited to generated answers, summaries, chat, and other features. *** ### domainsToOpenInCurrentTab?[​](#domainstoopenincurrenttab "Direct link to domainsToOpenInCurrentTab?") ``` optional domainsToOpenInCurrentTab?: string[]; ``` Always open links from these domains in the current tab. Expects a list of one or more domain suffixes, e.g. `['example.com', 'foo.example.org']`. *** ### enable3PCookieAccessRequest?[​](#enable3pcookieaccessrequest "Direct link to enable3PCookieAccessRequest?") ``` optional enable3PCookieAccessRequest?: boolean; ``` Enable flow to request the user for access if third-party cookies are blocked. If true, the user will be prompted to allow cookie access for glean.com when they try to login. Default to true since api version >= 2025-06-19. Note: This flag will have no impact if third-party cookies are already allowed. Users will be able to login as usual. Refer to [Guide to third-party cookies in Glean](https://developers.glean.com/docs/browser_api/third_party_cookies/) for more details. *** ### enableActivityLogging?[​](#enableactivitylogging "Direct link to enableActivityLogging?") ``` optional enableActivityLogging?: boolean; ``` Logs an activity event for each URL change of the embedding page. This improves search quality when embedded within a datasource for which there are not other activity signals (e.g. Salesforce). Note: if disableAnalytics is true, activity logging will be disabled regardless of the value of this option. *** ### externalSessionId?[​](#externalsessionid "Direct link to externalSessionId?") ``` optional externalSessionId?: string; ``` An external session identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own session telemetry. *** ### externalUserId?[​](#externaluserid "Direct link to externalUserId?") ``` optional externalUserId?: string; ``` An external user identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own user telemetry. *** ### fontFaces?[​](#fontfaces "Direct link to fontFaces?") ``` optional fontFaces?: FontFaceDetails[]; ``` A list of custom font-faces to include in the embedded page. When provided, the custom font-faces can be selected via the fontFamily option. *** ### fontFamily?[​](#fontfamily "Direct link to fontFamily?") ``` optional fontFamily?: string; ``` The font-family used for text in the embedded widget. *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: "medium" | "large" | "larger"; ``` The base font size of the embedded app. Defaults to 'medium' which corresponds to the browser's default font size, typically 16px. The actual font sizes of elements are relative to this value, some will be larger or smaller. *** ### key?[​](#key "Direct link to key?") ``` optional key?: string; ``` Uniquely identify the widget. Must be used if more than one Glean widget of the same type are rendered on the page Allowed characters: \[a-z A-Z - \_ . ; :] *** ### locale?[​](#locale "Direct link to locale?") ``` optional locale?: string; ``` Overrides the default locale for the embedded widget users. If 'auto' (default) is used, Glean will automatically detect the locale from user's browser. If an unsupported locale is provided, Glean will fallback to use the closest supported locale. If user picked their locale for Glean, their choice will be respected. *** ### onAuthTokenRequired?[​](#onauthtokenrequired "Direct link to onAuthTokenRequired?") ``` optional onAuthTokenRequired?: | (() => Promise) | (() => void); ``` A callback invoked when the user requires a new authentication token. Only invoked if an [Options.authToken](#authtoken) is provided initially and is nearing expiration. This option is required when [Options.authMethod](#authmethod) is 'token'. **Signatures:** 1. `() => Promise` - (Recommended) Return a Promise that resolves to the new token details. The SDK will update the token internally without requiring a re-render. #### Example[​](#example "Direct link to Example") ``` onAuthTokenRequired: async () => { const token = await fetchNewToken() return { token: token.value, expirationTime: token.expiresAt } } 2. `() => void` - (Legacy) The callback must fetch a new token and call the SDK's render method with updated options containing the new token. ``` *** ### theme?[​](#theme "Direct link to theme?") ``` optional theme?: Partial>; ``` A theme applied throughout the embedded widget. If a theme variant (e.g. dark) isn't supplied, the default Glean theme will be used when the user has applied that variant. If any color is not supplied for a variant, the default Glean color will be used. *** ### themeVariant?[​](#themevariant "Direct link to themeVariant?") ``` optional themeVariant?: ThemeVariantOrAuto; ``` Overrides the theme variant for the embedded widget (default: light) If 'auto' is selected, Glean will automatically switch between light and dark mode and remain in sync with the user device. *** ### unauthorizedMessage?[​](#unauthorizedmessage "Direct link to unauthorizedMessage?") ``` optional unauthorizedMessage?: string; ``` A message to display when the current session does not have valid authorization. The message only applies when [Options.authToken](#authtoken) is used ([Options.authMethod](#authmethod) is 'token'). It can be used to instruct the user on how to get back to an authorized session, e,g, "Please refresh the page" or "Sign in to \[SSO provider]". *** ### urlsToOpenInCurrentTab?[​](#urlstoopenincurrenttab "Direct link to urlsToOpenInCurrentTab?") ``` optional urlsToOpenInCurrentTab?: string[]; ``` Always open matching URLs in the current tab Should be an array of strings representing a regular expression. For example: "^https\://example\\.com.\*$" will match any URL that starts with ''. Rules: * Every regular expression MUST start with '^' to ensure it matches from the start of the URL. * Regular expressions do not include search params when matching the URL. * A maximum of 10 entries are allowed in the array. *** ### webAppUrl?[​](#webappurl "Direct link to webAppUrl?") ``` optional webAppUrl?: string; ``` The web page URL where users access Glean. For example: https\://\.glean.com. Admins can find this value at in the `Web app URL` field. --- # RecommendationsBoxCustomizations ## Properties[​](#properties "Direct link to Properties") ### border?[​](#border "Direct link to border?") ``` optional border?: string; ``` The CSS border of the widget box *** ### borderRadius?[​](#borderradius "Direct link to borderRadius?") ``` optional borderRadius?: number; ``` The border radius of the widget box *** ### boxShadow?[​](#boxshadow "Direct link to boxShadow?") ``` optional boxShadow?: string; ``` The CSS box-shadow of the widget box *** ### horizontalMargin?[​](#horizontalmargin "Direct link to horizontalMargin?") ``` optional horizontalMargin?: number; ``` A CSS margin to the left and right of the widget box *** ### searchBox?[​](#searchbox "Direct link to searchBox?") ``` optional searchBox?: Pick; ``` Optional style customizations for the search box *** ### showNoRecommendationsHint?[​](#shownorecommendationshint "Direct link to showNoRecommendationsHint?") ``` optional showNoRecommendationsHint?: boolean; ``` Whether a hint should be displayed when no recommendations are present. default: true *** ### verticalMargin?[​](#verticalmargin "Direct link to verticalMargin?") ``` optional verticalMargin?: number; ``` A CSS margin above and below the widget box --- # RecommendationsOptions ## Extends[​](#extends "Direct link to Extends") * [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`RecommendationsSearchOptions`](/libraries/web-sdk/reference/interfaces/RecommendationsSearchOptions.md).[`RecommendationsResultsOptions`](/libraries/web-sdk/reference/interfaces/RecommendationsResultsOptions.md) ## Properties[​](#properties "Direct link to Properties") ### authMethod?[​](#authmethod "Direct link to authMethod?") ``` optional authMethod?: "sso" | "token"; ``` The authentication method to use in the embedded widget. sso - Logged out users will see a login button in the embedded widget. Clicking the button will open the SSO login flow in a new window. token - The embedding page obtains an auth token on behalf of the user and passes it to the embedded widget. If this method is specified the [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) and [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) options must also be specified. #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`authMethod`](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) *** ### authToken?[​](#authtoken "Direct link to authToken?") ``` optional authToken?: AuthTokenDetails; ``` An authentication token for the user. Providing this obviates the need for the user to separately login to Glean. May be obtained via: If an authToken is provided, [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) must also be provided. #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`authToken`](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) *** ### backend?[​](#backend "Direct link to backend?") ``` optional backend?: string; ``` Overrides the search backend server to use. Setting this explicitly is always recommended as it alleviates the user from from having to manually enter their email address in order to route to the backend instance to log into. It's also useful for routing users to a production vs staging server (if available). Example: `https://{your}-be.glean.com/` #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`backend`](/libraries/web-sdk/reference/interfaces/Options.md#backend) *** ### customizations?[​](#customizations "Direct link to customizations?") ``` optional customizations?: RecommendationsBoxCustomizations; ``` Optional styling of the widget box. When adding boxShadow, border provide enough margin so they are visible *** ### datasource?[​](#datasource "Direct link to datasource?") ``` optional datasource?: string; ``` Select the search tab for the given datasource (e.g. `'confluence'`, `'gdrive'`, `'people'`) instead of the all tab. Other datasource tabs will be shown if results are available for the current query. See also [SearchOptions.datasourcesFilter](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter). #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasource`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource) *** ### datasourcesFilter?[​](#datasourcesfilter "Direct link to datasourcesFilter?") ``` optional datasourcesFilter?: string[]; ``` Filter search results to only the given list of datasources (e.g. `['figma']`, `['jira', 'zendesk']`). Results from other datasources will not be available. See also [SearchOptions.datasource](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource). #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasourcesFilter`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter) *** ### disableAnalytics?[​](#disableanalytics "Direct link to disableAnalytics?") ``` optional disableAnalytics?: boolean; ``` If true, Glean will not collect client analytics or log activity for the embedded session. Analytics data is used to monitor and improve the product experience, so it's recommended to leave data collection on unless the user has specifically requested otherwise. #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`disableAnalytics`](/libraries/web-sdk/reference/interfaces/Options.md#disableanalytics) *** ### disableAssistant?[​](#disableassistant "Direct link to disableAssistant?") ``` optional disableAssistant?: boolean; ``` If true, Glean Assistant will not be enabled for the embedded session, including but not limited to generated answers, summaries, chat, and other features. #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`disableAssistant`](/libraries/web-sdk/reference/interfaces/Options.md#disableassistant) *** ### domainsToOpenInCurrentTab?[​](#domainstoopenincurrenttab "Direct link to domainsToOpenInCurrentTab?") ``` optional domainsToOpenInCurrentTab?: string[]; ``` Always open links from these domains in the current tab. Expects a list of one or more domain suffixes, e.g. `['example.com', 'foo.example.org']`. #### Inherited from[​](#inherited-from-7 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`domainsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/Options.md#domainstoopenincurrenttab) *** ### enable3PCookieAccessRequest?[​](#enable3pcookieaccessrequest "Direct link to enable3PCookieAccessRequest?") ``` optional enable3PCookieAccessRequest?: boolean; ``` Enable flow to request the user for access if third-party cookies are blocked. If true, the user will be prompted to allow cookie access for glean.com when they try to login. Default to true since api version >= 2025-06-19. Note: This flag will have no impact if third-party cookies are already allowed. Users will be able to login as usual. Refer to [Guide to third-party cookies in Glean](https://developers.glean.com/docs/browser_api/third_party_cookies/) for more details. #### Inherited from[​](#inherited-from-8 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`enable3PCookieAccessRequest`](/libraries/web-sdk/reference/interfaces/Options.md#enable3pcookieaccessrequest) *** ### enableActivityLogging?[​](#enableactivitylogging "Direct link to enableActivityLogging?") ``` optional enableActivityLogging?: boolean; ``` Logs an activity event for each URL change of the embedding page. This improves search quality when embedded within a datasource for which there are not other activity signals (e.g. Salesforce). Note: if disableAnalytics is true, activity logging will be disabled regardless of the value of this option. #### Inherited from[​](#inherited-from-9 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`enableActivityLogging`](/libraries/web-sdk/reference/interfaces/Options.md#enableactivitylogging) *** ### externalSessionId?[​](#externalsessionid "Direct link to externalSessionId?") ``` optional externalSessionId?: string; ``` An external session identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own session telemetry. #### Inherited from[​](#inherited-from-10 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`externalSessionId`](/libraries/web-sdk/reference/interfaces/Options.md#externalsessionid) *** ### externalUserId?[​](#externaluserid "Direct link to externalUserId?") ``` optional externalUserId?: string; ``` An external user identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own user telemetry. #### Inherited from[​](#inherited-from-11 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`externalUserId`](/libraries/web-sdk/reference/interfaces/Options.md#externaluserid) *** ### fontFaces?[​](#fontfaces "Direct link to fontFaces?") ``` optional fontFaces?: FontFaceDetails[]; ``` A list of custom font-faces to include in the embedded page. When provided, the custom font-faces can be selected via the fontFamily option. #### Inherited from[​](#inherited-from-12 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontFaces`](/libraries/web-sdk/reference/interfaces/Options.md#fontfaces) *** ### fontFamily?[​](#fontfamily "Direct link to fontFamily?") ``` optional fontFamily?: string; ``` The font-family used for text in the embedded widget. #### Inherited from[​](#inherited-from-13 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontFamily`](/libraries/web-sdk/reference/interfaces/Options.md#fontfamily) *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: "medium" | "large" | "larger"; ``` The base font size of the embedded app. Defaults to 'medium' which corresponds to the browser's default font size, typically 16px. The actual font sizes of elements are relative to this value, some will be larger or smaller. #### Inherited from[​](#inherited-from-14 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontSize`](/libraries/web-sdk/reference/interfaces/Options.md#fontsize) *** ### height?[​](#height "Direct link to height?") ``` optional height?: number; ``` Determines the total height of the widget in the open state. If not provided 650px is taken as default. *** ### hideAutocomplete?[​](#hideautocomplete "Direct link to hideAutocomplete?") ``` optional hideAutocomplete?: boolean; ``` If true, search boxes will not render the autocomplete dropdown. #### Inherited from[​](#inherited-from-15 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`hideAutocomplete`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#hideautocomplete) *** ### hideDatasourceFilter?[​](#hidedatasourcefilter "Direct link to hideDatasourceFilter?") ``` optional hideDatasourceFilter?: boolean; ``` If true, hide the datasource filter in the righthand column of the search results page. #### Inherited from[​](#inherited-from-16 "Direct link to Inherited from") [`TabbedSearchOptions`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md).[`hideDatasourceFilter`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md#hidedatasourcefilter) *** ### key?[​](#key "Direct link to key?") ``` optional key?: string; ``` Uniquely identify the widget. Must be used if more than one Glean widget of the same type are rendered on the page Allowed characters: \[a-z A-Z - \_ . ; :] #### Inherited from[​](#inherited-from-17 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`key`](/libraries/web-sdk/reference/interfaces/Options.md#key) *** ### locale?[​](#locale "Direct link to locale?") ``` optional locale?: string; ``` Overrides the default locale for the embedded widget users. If 'auto' (default) is used, Glean will automatically detect the locale from user's browser. If an unsupported locale is provided, Glean will fallback to use the closest supported locale. If user picked their locale for Glean, their choice will be respected. #### Inherited from[​](#inherited-from-18 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`locale`](/libraries/web-sdk/reference/interfaces/Options.md#locale) *** ### onAuthTokenRequired?[​](#onauthtokenrequired "Direct link to onAuthTokenRequired?") ``` optional onAuthTokenRequired?: | (() => Promise) | (() => void); ``` A callback invoked when the user requires a new authentication token. Only invoked if an [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is provided initially and is nearing expiration. This option is required when [Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'. **Signatures:** 1. `() => Promise` - (Recommended) Return a Promise that resolves to the new token details. The SDK will update the token internally without requiring a re-render. #### Example[​](#example "Direct link to Example") ``` onAuthTokenRequired: async () => { const token = await fetchNewToken() return { token: token.value, expirationTime: token.expiresAt } } 2. `() => void` - (Legacy) The callback must fetch a new token and call the SDK's render method with updated options containing the new token. ``` #### Inherited from[​](#inherited-from-19 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`onAuthTokenRequired`](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) *** ### onDatasourceChange?[​](#ondatasourcechange "Direct link to onDatasourceChange?") ``` optional onDatasourceChange?: (datasource?) => void; ``` A callback invoked when the user changes search tabs to a different datasource. If `undefined`, all datasources are shown. #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | | ------------- | -------- | | `datasource?` | `string` | #### Returns[​](#returns "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-20 "Direct link to Inherited from") [`TabbedSearchOptions`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md).[`onDatasourceChange`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md#ondatasourcechange) *** ### onSearch[​](#onsearch "Direct link to onSearch") ``` onSearch: (query) => void; ``` A callback invoked when the user performs a search query. #### Parameters[​](#parameters-1 "Direct link to Parameters") | Parameter | Type | | --------- | -------- | | `query` | `string` | #### Returns[​](#returns-1 "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-21 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onSearch`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) *** ### sourceDocument?[​](#sourcedocument "Direct link to sourceDocument?") ``` optional sourceDocument?: DocumentContext; ``` Information about the current document for which Glean widget will show recommendations. If not provided, the embedder page info is used. *** ### theme?[​](#theme "Direct link to theme?") ``` optional theme?: Partial>; ``` A theme applied throughout the embedded widget. If a theme variant (e.g. dark) isn't supplied, the default Glean theme will be used when the user has applied that variant. If any color is not supplied for a variant, the default Glean color will be used. #### Inherited from[​](#inherited-from-22 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`theme`](/libraries/web-sdk/reference/interfaces/Options.md#theme) *** ### themeVariant?[​](#themevariant "Direct link to themeVariant?") ``` optional themeVariant?: ThemeVariantOrAuto; ``` Overrides the theme variant for the embedded widget (default: light) If 'auto' is selected, Glean will automatically switch between light and dark mode and remain in sync with the user device. #### Inherited from[​](#inherited-from-23 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`themeVariant`](/libraries/web-sdk/reference/interfaces/Options.md#themevariant) *** ### unauthorizedMessage?[​](#unauthorizedmessage "Direct link to unauthorizedMessage?") ``` optional unauthorizedMessage?: string; ``` A message to display when the current session does not have valid authorization. The message only applies when [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is used ([Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'). It can be used to instruct the user on how to get back to an authorized session, e,g, "Please refresh the page" or "Sign in to \[SSO provider]". #### Inherited from[​](#inherited-from-24 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`unauthorizedMessage`](/libraries/web-sdk/reference/interfaces/Options.md#unauthorizedmessage) *** ### urlsToOpenInCurrentTab?[​](#urlstoopenincurrenttab "Direct link to urlsToOpenInCurrentTab?") ``` optional urlsToOpenInCurrentTab?: string[]; ``` Always open matching URLs in the current tab Should be an array of strings representing a regular expression. For example: "^https\://example\\.com.\*$" will match any URL that starts with ''. Rules: * Every regular expression MUST start with '^' to ensure it matches from the start of the URL. * Regular expressions do not include search params when matching the URL. * A maximum of 10 entries are allowed in the array. #### Inherited from[​](#inherited-from-25 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`urlsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/Options.md#urlstoopenincurrenttab) *** ### webAppUrl?[​](#webappurl "Direct link to webAppUrl?") ``` optional webAppUrl?: string; ``` The web page URL where users access Glean. For example: https\://\.glean.com. Admins can find this value at in the `Web app URL` field. #### Inherited from[​](#inherited-from-26 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`webAppUrl`](/libraries/web-sdk/reference/interfaces/Options.md#webappurl) --- # RecommendationsResultsOptions ## Extends[​](#extends "Direct link to Extends") * `Pick`<[`TabbedSearchOptions`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md), `"hideDatasourceFilter"` | `"onDatasourceChange"`> ## Extended by[​](#extended-by "Direct link to Extended by") * [`RecommendationsOptions`](/libraries/web-sdk/reference/interfaces/RecommendationsOptions.md) ## Properties[​](#properties "Direct link to Properties") ### hideDatasourceFilter?[​](#hidedatasourcefilter "Direct link to hideDatasourceFilter?") ``` optional hideDatasourceFilter?: boolean; ``` If true, hide the datasource filter in the righthand column of the search results page. #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`TabbedSearchOptions`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md).[`hideDatasourceFilter`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md#hidedatasourcefilter) *** ### onDatasourceChange?[​](#ondatasourcechange "Direct link to onDatasourceChange?") ``` optional onDatasourceChange?: (datasource?) => void; ``` A callback invoked when the user changes search tabs to a different datasource. If `undefined`, all datasources are shown. #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | | ------------- | -------- | | `datasource?` | `string` | #### Returns[​](#returns "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`TabbedSearchOptions`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md).[`onDatasourceChange`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md#ondatasourcechange) --- # RecommendationsSearchOptions ## Extends[​](#extends "Direct link to Extends") * `Pick`<[`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md), `"hideAutocomplete"` | `"onSearch"` | `"datasource"` | `"datasourcesFilter"`> ## Extended by[​](#extended-by "Direct link to Extended by") * [`RecommendationsOptions`](/libraries/web-sdk/reference/interfaces/RecommendationsOptions.md) ## Properties[​](#properties "Direct link to Properties") ### datasource?[​](#datasource "Direct link to datasource?") ``` optional datasource?: string; ``` Select the search tab for the given datasource (e.g. `'confluence'`, `'gdrive'`, `'people'`) instead of the all tab. Other datasource tabs will be shown if results are available for the current query. See also [SearchOptions.datasourcesFilter](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter). #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasource`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource) *** ### datasourcesFilter?[​](#datasourcesfilter "Direct link to datasourcesFilter?") ``` optional datasourcesFilter?: string[]; ``` Filter search results to only the given list of datasources (e.g. `['figma']`, `['jira', 'zendesk']`). Results from other datasources will not be available. See also [SearchOptions.datasource](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource). #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasourcesFilter`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter) *** ### hideAutocomplete?[​](#hideautocomplete "Direct link to hideAutocomplete?") ``` optional hideAutocomplete?: boolean; ``` If true, search boxes will not render the autocomplete dropdown. #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`hideAutocomplete`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#hideautocomplete) *** ### onSearch[​](#onsearch "Direct link to onSearch") ``` onSearch: (query) => void; ``` A callback invoked when the user performs a search query. #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | | --------- | -------- | | `query` | `string` | #### Returns[​](#returns "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onSearch`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) --- # SearchBoxCustomizations Style and other options which can be customized on the embedded search widget box. ## Extends[​](#extends "Direct link to Extends") * [`BoxCustomizations`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md) ## Properties[​](#properties "Direct link to Properties") ### backgroundColor?[​](#backgroundcolor "Direct link to backgroundColor?") ``` optional backgroundColor?: string; ``` The background color of the search box. Default: transparent. *** ### border?[​](#border "Direct link to border?") ``` optional border?: string; ``` The CSS border of the widget box #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`BoxCustomizations`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md).[`border`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md#border) *** ### borderRadius?[​](#borderradius "Direct link to borderRadius?") ``` optional borderRadius?: number; ``` The border radius of the widget box #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`BoxCustomizations`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md).[`borderRadius`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md#borderradius) *** ### boxShadow?[​](#boxshadow "Direct link to boxShadow?") ``` optional boxShadow?: string; ``` The CSS box-shadow of the widget box #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`BoxCustomizations`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md).[`boxShadow`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md#boxshadow) *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: number; ``` The size of the placeholder and query text. Default: 16px. *** ### horizontalMargin?[​](#horizontalmargin "Direct link to horizontalMargin?") ``` optional horizontalMargin?: number; ``` A CSS margin to the left and right of the widget box #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`BoxCustomizations`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md).[`horizontalMargin`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md#horizontalmargin) *** ### placeholderText?[​](#placeholdertext "Direct link to placeholderText?") ``` optional placeholderText?: string; ``` Text to display in the search box before a query is entered *** ### searchIconUrl?[​](#searchiconurl "Direct link to searchIconUrl?") ``` optional searchIconUrl?: string; ``` An https: or data: URL for an image to display instead of the default search icon (magnifying glass) *** ### verticalMargin?[​](#verticalmargin "Direct link to verticalMargin?") ``` optional verticalMargin?: number; ``` A CSS margin above and below the widget box #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from") [`BoxCustomizations`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md).[`verticalMargin`](/libraries/web-sdk/reference/interfaces/BoxCustomizations.md#verticalmargin) --- # SearchBoxOptions ## Extends[​](#extends "Direct link to Extends") * [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md) ## Properties[​](#properties "Direct link to Properties") ### authMethod?[​](#authmethod "Direct link to authMethod?") ``` optional authMethod?: "sso" | "token"; ``` The authentication method to use in the embedded widget. sso - Logged out users will see a login button in the embedded widget. Clicking the button will open the SSO login flow in a new window. token - The embedding page obtains an auth token on behalf of the user and passes it to the embedded widget. If this method is specified the [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) and [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) options must also be specified. #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`authMethod`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#authmethod) *** ### authToken?[​](#authtoken "Direct link to authToken?") ``` optional authToken?: AuthTokenDetails; ``` An authentication token for the user. Providing this obviates the need for the user to separately login to Glean. May be obtained via: If an authToken is provided, [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) must also be provided. #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`authToken`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#authtoken) *** ### autofocus?[​](#autofocus "Direct link to autofocus?") ``` optional autofocus?: boolean; ``` Specifies whether the autocomplete box should automatically get focus when initially displayed. Default value is false. Note: When using the autofocus prop, consider accessibility concerns as it can be disorienting for visually-impaired users relying on screen readers and may cause unexpected scrolling and keyboard display on touch devices. Check [autofocus accessibility considerations](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus#accessibility_considerations) *** ### backend?[​](#backend "Direct link to backend?") ``` optional backend?: string; ``` Overrides the search backend server to use. Setting this explicitly is always recommended as it alleviates the user from from having to manually enter their email address in order to route to the backend instance to log into. It's also useful for routing users to a production vs staging server (if available). Example: `https://{your}-be.glean.com/` #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`backend`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#backend) *** ### datasource?[​](#datasource "Direct link to datasource?") ``` optional datasource?: string; ``` Select the search tab for the given datasource (e.g. `'confluence'`, `'gdrive'`, `'people'`) instead of the all tab. Other datasource tabs will be shown if results are available for the current query. See also [SearchOptions.datasourcesFilter](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter). #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasource`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource) *** ### datasourcesFilter?[​](#datasourcesfilter "Direct link to datasourcesFilter?") ``` optional datasourcesFilter?: string[]; ``` Filter search results to only the given list of datasources (e.g. `['figma']`, `['jira', 'zendesk']`). Results from other datasources will not be available. See also [SearchOptions.datasource](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource). #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasourcesFilter`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter) *** ### disableAnalytics?[​](#disableanalytics "Direct link to disableAnalytics?") ``` optional disableAnalytics?: boolean; ``` If true, Glean will not collect client analytics or log activity for the embedded session. Analytics data is used to monitor and improve the product experience, so it's recommended to leave data collection on unless the user has specifically requested otherwise. #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`disableAnalytics`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#disableanalytics) *** ### disableAssistant?[​](#disableassistant "Direct link to disableAssistant?") ``` optional disableAssistant?: boolean; ``` If true, Glean Assistant will not be enabled for the embedded session, including but not limited to generated answers, summaries, chat, and other features. #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`disableAssistant`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#disableassistant) *** ### domainsToOpenInCurrentTab?[​](#domainstoopenincurrenttab "Direct link to domainsToOpenInCurrentTab?") ``` optional domainsToOpenInCurrentTab?: string[]; ``` Always open links from these domains in the current tab. Expects a list of one or more domain suffixes, e.g. `['example.com', 'foo.example.org']`. #### Inherited from[​](#inherited-from-7 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`domainsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#domainstoopenincurrenttab) *** ### enable3PCookieAccessRequest?[​](#enable3pcookieaccessrequest "Direct link to enable3PCookieAccessRequest?") ``` optional enable3PCookieAccessRequest?: boolean; ``` Enable flow to request the user for access if third-party cookies are blocked. If true, the user will be prompted to allow cookie access for glean.com when they try to login. Default to true since api version >= 2025-06-19. Note: This flag will have no impact if third-party cookies are already allowed. Users will be able to login as usual. Refer to [Guide to third-party cookies in Glean](https://developers.glean.com/docs/browser_api/third_party_cookies/) for more details. #### Inherited from[​](#inherited-from-8 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`enable3PCookieAccessRequest`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#enable3pcookieaccessrequest) *** ### enableActivityLogging?[​](#enableactivitylogging "Direct link to enableActivityLogging?") ``` optional enableActivityLogging?: boolean; ``` Logs an activity event for each URL change of the embedding page. This improves search quality when embedded within a datasource for which there are not other activity signals (e.g. Salesforce). Note: if disableAnalytics is true, activity logging will be disabled regardless of the value of this option. #### Inherited from[​](#inherited-from-9 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`enableActivityLogging`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#enableactivitylogging) *** ### externalSessionId?[​](#externalsessionid "Direct link to externalSessionId?") ``` optional externalSessionId?: string; ``` An external session identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own session telemetry. #### Inherited from[​](#inherited-from-10 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`externalSessionId`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#externalsessionid) *** ### externalUserId?[​](#externaluserid "Direct link to externalUserId?") ``` optional externalUserId?: string; ``` An external user identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own user telemetry. #### Inherited from[​](#inherited-from-11 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`externalUserId`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#externaluserid) *** ### filters?[​](#filters "Direct link to filters?") ``` optional filters?: FilterValue[]; ``` Filters applied on all searches done by the user. Any filters in this list will override any manually input filters from users or filters defined in [initialFilters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) that share the same key. These filters are non-modifiable by the user, and will not show up in the UI as being applied. For example, if a filter with some key `keyA` is specified here, then: * Users will not be able to modify any filters corresponding to `keyA` in the UI in any way * The filter dropdown for that key will not show up on the UI (nor in the overflow button) * Search bar operators (i.e. `keyA:[value]`) with that key will be overridden and removed from the search bar query * Any filters defined in [initialFilters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) with `keyA` will be ignored. When specifying the list of filters the same `key` may be specified more than once, in which case the `value`s for that key are combined via an 'OR' operation. The `value`s for different `key`s are combined via 'AND'. For example, this set of filters will match all results that were updated in the past week AND are of type "bug" OR "task": ``` [{ key: "type", value: "bug" }, { key: "type", value: "task" }, { key: "updated", value: "past_week" }] ``` #### Inherited from[​](#inherited-from-12 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`filters`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#filters) *** ### fontFaces?[​](#fontfaces "Direct link to fontFaces?") ``` optional fontFaces?: FontFaceDetails[]; ``` A list of custom font-faces to include in the embedded page. When provided, the custom font-faces can be selected via the fontFamily option. #### Inherited from[​](#inherited-from-13 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontFaces`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontfaces) *** ### fontFamily?[​](#fontfamily "Direct link to fontFamily?") ``` optional fontFamily?: string; ``` The font-family used for text in the embedded widget. #### Inherited from[​](#inherited-from-14 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontFamily`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontfamily) *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: "medium" | "large" | "larger"; ``` The base font size of the embedded app. Defaults to 'medium' which corresponds to the browser's default font size, typically 16px. The actual font sizes of elements are relative to this value, some will be larger or smaller. #### Inherited from[​](#inherited-from-15 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontSize`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontsize) *** ### hideAutocomplete?[​](#hideautocomplete "Direct link to hideAutocomplete?") ``` optional hideAutocomplete?: boolean; ``` If true, search boxes will not render the autocomplete dropdown. #### Inherited from[​](#inherited-from-16 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`hideAutocomplete`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#hideautocomplete) *** ### ~~initialFilters?~~[​](#initialfilters "Direct link to initialfilters") ``` optional initialFilters?: FilterValue[]; ``` #### Deprecated[​](#deprecated "Direct link to Deprecated") Use [filters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#filters) instead. #### Inherited from[​](#inherited-from-17 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`initialFilters`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) *** ### key?[​](#key "Direct link to key?") ``` optional key?: string; ``` Uniquely identify the widget. Must be used if more than one Glean widget of the same type are rendered on the page Allowed characters: \[a-z A-Z - \_ . ; :] #### Inherited from[​](#inherited-from-18 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`key`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#key) *** ### locale?[​](#locale "Direct link to locale?") ``` optional locale?: string; ``` Overrides the default locale for the embedded widget users. If 'auto' (default) is used, Glean will automatically detect the locale from user's browser. If an unsupported locale is provided, Glean will fallback to use the closest supported locale. If user picked their locale for Glean, their choice will be respected. #### Inherited from[​](#inherited-from-19 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`locale`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#locale) *** ### onAuthTokenRequired?[​](#onauthtokenrequired "Direct link to onAuthTokenRequired?") ``` optional onAuthTokenRequired?: | (() => Promise) | (() => void); ``` A callback invoked when the user requires a new authentication token. Only invoked if an [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is provided initially and is nearing expiration. This option is required when [Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'. **Signatures:** 1. `() => Promise` - (Recommended) Return a Promise that resolves to the new token details. The SDK will update the token internally without requiring a re-render. #### Example[​](#example "Direct link to Example") ``` onAuthTokenRequired: async () => { const token = await fetchNewToken() return { token: token.value, expirationTime: token.expiresAt } } 2. `() => void` - (Legacy) The callback must fetch a new token and call the SDK's render method with updated options containing the new token. ``` #### Inherited from[​](#inherited-from-20 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onAuthTokenRequired`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onauthtokenrequired) *** ### onChat?[​](#onchat "Direct link to onChat?") ``` optional onChat?: (chatId?) => void; ``` A callback invoked when the user tries to initiate a chat from search pages, like follow ups from AI Answer. If provided, we will allow and show follow ups on AI Answer for user to continue to chat from there, typical usage can be: 1. open the chat in a new page 2. or open chat with `renderChat` method on the same page #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | -------- | -------------------------------------------------------------------------------------- | | `chatId?` | `string` | An opaque id that stores the context of the current / new chat for the current session | #### Returns[​](#returns "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-21 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onChat`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onchat) *** ### onSearch[​](#onsearch "Direct link to onSearch") ``` onSearch: (query) => void; ``` A callback invoked when the user performs a search query. #### Parameters[​](#parameters-1 "Direct link to Parameters") | Parameter | Type | | --------- | -------- | | `query` | `string` | #### Returns[​](#returns-1 "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-22 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onSearch`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) *** ### query?[​](#query "Direct link to query?") ``` optional query?: string; ``` The initial query text to perform/display. #### Inherited from[​](#inherited-from-23 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`query`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#query) *** ### searchBoxCustomizations?[​](#searchboxcustomizations "Direct link to searchBoxCustomizations?") ``` optional searchBoxCustomizations?: SearchBoxCustomizations; ``` Optional style customizations for the search box. *** ### theme?[​](#theme "Direct link to theme?") ``` optional theme?: Partial>; ``` A theme applied throughout the embedded widget. If a theme variant (e.g. dark) isn't supplied, the default Glean theme will be used when the user has applied that variant. If any color is not supplied for a variant, the default Glean color will be used. #### Inherited from[​](#inherited-from-24 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`theme`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#theme) *** ### themeVariant?[​](#themevariant "Direct link to themeVariant?") ``` optional themeVariant?: ThemeVariantOrAuto; ``` Overrides the theme variant for the embedded widget (default: light) If 'auto' is selected, Glean will automatically switch between light and dark mode and remain in sync with the user device. #### Inherited from[​](#inherited-from-25 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`themeVariant`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#themevariant) *** ### unauthorizedMessage?[​](#unauthorizedmessage "Direct link to unauthorizedMessage?") ``` optional unauthorizedMessage?: string; ``` A message to display when the current session does not have valid authorization. The message only applies when [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is used ([Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'). It can be used to instruct the user on how to get back to an authorized session, e,g, "Please refresh the page" or "Sign in to \[SSO provider]". #### Inherited from[​](#inherited-from-26 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`unauthorizedMessage`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#unauthorizedmessage) *** ### urlsToOpenInCurrentTab?[​](#urlstoopenincurrenttab "Direct link to urlsToOpenInCurrentTab?") ``` optional urlsToOpenInCurrentTab?: string[]; ``` Always open matching URLs in the current tab Should be an array of strings representing a regular expression. For example: "^https\://example\\.com.\*$" will match any URL that starts with ''. Rules: * Every regular expression MUST start with '^' to ensure it matches from the start of the URL. * Regular expressions do not include search params when matching the URL. * A maximum of 10 entries are allowed in the array. #### Inherited from[​](#inherited-from-27 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`urlsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#urlstoopenincurrenttab) *** ### webAppUrl?[​](#webappurl "Direct link to webAppUrl?") ``` optional webAppUrl?: string; ``` The web page URL where users access Glean. For example: https\://\.glean.com. Admins can find this value at in the `Web app URL` field. #### Inherited from[​](#inherited-from-28 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`webAppUrl`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#webappurl) --- # SearchOptions ## Extends[​](#extends "Direct link to Extends") * [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) ## Extended by[​](#extended-by "Direct link to Extended by") * [`SearchBoxOptions`](/libraries/web-sdk/reference/interfaces/SearchBoxOptions.md) * [`ModalSearchOptions`](/libraries/web-sdk/reference/interfaces/ModalSearchOptions.md) * [`TabbedSearchOptions`](/libraries/web-sdk/reference/interfaces/TabbedSearchOptions.md) ## Properties[​](#properties "Direct link to Properties") ### authMethod?[​](#authmethod "Direct link to authMethod?") ``` optional authMethod?: "sso" | "token"; ``` The authentication method to use in the embedded widget. sso - Logged out users will see a login button in the embedded widget. Clicking the button will open the SSO login flow in a new window. token - The embedding page obtains an auth token on behalf of the user and passes it to the embedded widget. If this method is specified the [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) and [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) options must also be specified. #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`authMethod`](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) *** ### authToken?[​](#authtoken "Direct link to authToken?") ``` optional authToken?: AuthTokenDetails; ``` An authentication token for the user. Providing this obviates the need for the user to separately login to Glean. May be obtained via: If an authToken is provided, [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) must also be provided. #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`authToken`](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) *** ### backend?[​](#backend "Direct link to backend?") ``` optional backend?: string; ``` Overrides the search backend server to use. Setting this explicitly is always recommended as it alleviates the user from from having to manually enter their email address in order to route to the backend instance to log into. It's also useful for routing users to a production vs staging server (if available). Example: `https://{your}-be.glean.com/` #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`backend`](/libraries/web-sdk/reference/interfaces/Options.md#backend) *** ### datasource?[​](#datasource "Direct link to datasource?") ``` optional datasource?: string; ``` Select the search tab for the given datasource (e.g. `'confluence'`, `'gdrive'`, `'people'`) instead of the all tab. Other datasource tabs will be shown if results are available for the current query. See also [SearchOptions.datasourcesFilter](#datasourcesfilter). *** ### datasourcesFilter?[​](#datasourcesfilter "Direct link to datasourcesFilter?") ``` optional datasourcesFilter?: string[]; ``` Filter search results to only the given list of datasources (e.g. `['figma']`, `['jira', 'zendesk']`). Results from other datasources will not be available. See also [SearchOptions.datasource](#datasource). *** ### disableAnalytics?[​](#disableanalytics "Direct link to disableAnalytics?") ``` optional disableAnalytics?: boolean; ``` If true, Glean will not collect client analytics or log activity for the embedded session. Analytics data is used to monitor and improve the product experience, so it's recommended to leave data collection on unless the user has specifically requested otherwise. #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`disableAnalytics`](/libraries/web-sdk/reference/interfaces/Options.md#disableanalytics) *** ### disableAssistant?[​](#disableassistant "Direct link to disableAssistant?") ``` optional disableAssistant?: boolean; ``` If true, Glean Assistant will not be enabled for the embedded session, including but not limited to generated answers, summaries, chat, and other features. #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`disableAssistant`](/libraries/web-sdk/reference/interfaces/Options.md#disableassistant) *** ### domainsToOpenInCurrentTab?[​](#domainstoopenincurrenttab "Direct link to domainsToOpenInCurrentTab?") ``` optional domainsToOpenInCurrentTab?: string[]; ``` Always open links from these domains in the current tab. Expects a list of one or more domain suffixes, e.g. `['example.com', 'foo.example.org']`. #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`domainsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/Options.md#domainstoopenincurrenttab) *** ### enable3PCookieAccessRequest?[​](#enable3pcookieaccessrequest "Direct link to enable3PCookieAccessRequest?") ``` optional enable3PCookieAccessRequest?: boolean; ``` Enable flow to request the user for access if third-party cookies are blocked. If true, the user will be prompted to allow cookie access for glean.com when they try to login. Default to true since api version >= 2025-06-19. Note: This flag will have no impact if third-party cookies are already allowed. Users will be able to login as usual. Refer to [Guide to third-party cookies in Glean](https://developers.glean.com/docs/browser_api/third_party_cookies/) for more details. #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`enable3PCookieAccessRequest`](/libraries/web-sdk/reference/interfaces/Options.md#enable3pcookieaccessrequest) *** ### enableActivityLogging?[​](#enableactivitylogging "Direct link to enableActivityLogging?") ``` optional enableActivityLogging?: boolean; ``` Logs an activity event for each URL change of the embedding page. This improves search quality when embedded within a datasource for which there are not other activity signals (e.g. Salesforce). Note: if disableAnalytics is true, activity logging will be disabled regardless of the value of this option. #### Inherited from[​](#inherited-from-7 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`enableActivityLogging`](/libraries/web-sdk/reference/interfaces/Options.md#enableactivitylogging) *** ### externalSessionId?[​](#externalsessionid "Direct link to externalSessionId?") ``` optional externalSessionId?: string; ``` An external session identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own session telemetry. #### Inherited from[​](#inherited-from-8 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`externalSessionId`](/libraries/web-sdk/reference/interfaces/Options.md#externalsessionid) *** ### externalUserId?[​](#externaluserid "Direct link to externalUserId?") ``` optional externalUserId?: string; ``` An external user identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own user telemetry. #### Inherited from[​](#inherited-from-9 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`externalUserId`](/libraries/web-sdk/reference/interfaces/Options.md#externaluserid) *** ### filters?[​](#filters "Direct link to filters?") ``` optional filters?: FilterValue[]; ``` Filters applied on all searches done by the user. Any filters in this list will override any manually input filters from users or filters defined in [initialFilters](#initialfilters) that share the same key. These filters are non-modifiable by the user, and will not show up in the UI as being applied. For example, if a filter with some key `keyA` is specified here, then: * Users will not be able to modify any filters corresponding to `keyA` in the UI in any way * The filter dropdown for that key will not show up on the UI (nor in the overflow button) * Search bar operators (i.e. `keyA:[value]`) with that key will be overridden and removed from the search bar query * Any filters defined in [initialFilters](#initialfilters) with `keyA` will be ignored. When specifying the list of filters the same `key` may be specified more than once, in which case the `value`s for that key are combined via an 'OR' operation. The `value`s for different `key`s are combined via 'AND'. For example, this set of filters will match all results that were updated in the past week AND are of type "bug" OR "task": ``` [{ key: "type", value: "bug" }, { key: "type", value: "task" }, { key: "updated", value: "past_week" }] ``` *** ### fontFaces?[​](#fontfaces "Direct link to fontFaces?") ``` optional fontFaces?: FontFaceDetails[]; ``` A list of custom font-faces to include in the embedded page. When provided, the custom font-faces can be selected via the fontFamily option. #### Inherited from[​](#inherited-from-10 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontFaces`](/libraries/web-sdk/reference/interfaces/Options.md#fontfaces) *** ### fontFamily?[​](#fontfamily "Direct link to fontFamily?") ``` optional fontFamily?: string; ``` The font-family used for text in the embedded widget. #### Inherited from[​](#inherited-from-11 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontFamily`](/libraries/web-sdk/reference/interfaces/Options.md#fontfamily) *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: "medium" | "large" | "larger"; ``` The base font size of the embedded app. Defaults to 'medium' which corresponds to the browser's default font size, typically 16px. The actual font sizes of elements are relative to this value, some will be larger or smaller. #### Inherited from[​](#inherited-from-12 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontSize`](/libraries/web-sdk/reference/interfaces/Options.md#fontsize) *** ### hideAutocomplete?[​](#hideautocomplete "Direct link to hideAutocomplete?") ``` optional hideAutocomplete?: boolean; ``` If true, search boxes will not render the autocomplete dropdown. *** ### ~~initialFilters?~~[​](#initialfilters "Direct link to initialfilters") ``` optional initialFilters?: FilterValue[]; ``` #### Deprecated[​](#deprecated "Direct link to Deprecated") Use [filters](#filters) instead. *** ### key?[​](#key "Direct link to key?") ``` optional key?: string; ``` Uniquely identify the widget. Must be used if more than one Glean widget of the same type are rendered on the page Allowed characters: \[a-z A-Z - \_ . ; :] #### Inherited from[​](#inherited-from-13 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`key`](/libraries/web-sdk/reference/interfaces/Options.md#key) *** ### locale?[​](#locale "Direct link to locale?") ``` optional locale?: string; ``` Overrides the default locale for the embedded widget users. If 'auto' (default) is used, Glean will automatically detect the locale from user's browser. If an unsupported locale is provided, Glean will fallback to use the closest supported locale. If user picked their locale for Glean, their choice will be respected. #### Inherited from[​](#inherited-from-14 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`locale`](/libraries/web-sdk/reference/interfaces/Options.md#locale) *** ### onAuthTokenRequired?[​](#onauthtokenrequired "Direct link to onAuthTokenRequired?") ``` optional onAuthTokenRequired?: | (() => Promise) | (() => void); ``` A callback invoked when the user requires a new authentication token. Only invoked if an [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is provided initially and is nearing expiration. This option is required when [Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'. **Signatures:** 1. `() => Promise` - (Recommended) Return a Promise that resolves to the new token details. The SDK will update the token internally without requiring a re-render. #### Example[​](#example "Direct link to Example") ``` onAuthTokenRequired: async () => { const token = await fetchNewToken() return { token: token.value, expirationTime: token.expiresAt } } 2. `() => void` - (Legacy) The callback must fetch a new token and call the SDK's render method with updated options containing the new token. ``` #### Inherited from[​](#inherited-from-15 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`onAuthTokenRequired`](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) *** ### onChat?[​](#onchat "Direct link to onChat?") ``` optional onChat?: (chatId?) => void; ``` A callback invoked when the user tries to initiate a chat from search pages, like follow ups from AI Answer. If provided, we will allow and show follow ups on AI Answer for user to continue to chat from there, typical usage can be: 1. open the chat in a new page 2. or open chat with `renderChat` method on the same page #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | -------- | -------------------------------------------------------------------------------------- | | `chatId?` | `string` | An opaque id that stores the context of the current / new chat for the current session | #### Returns[​](#returns "Direct link to Returns") `void` *** ### onSearch[​](#onsearch "Direct link to onSearch") ``` onSearch: (query) => void; ``` A callback invoked when the user performs a search query. #### Parameters[​](#parameters-1 "Direct link to Parameters") | Parameter | Type | | --------- | -------- | | `query` | `string` | #### Returns[​](#returns-1 "Direct link to Returns") `void` *** ### query?[​](#query "Direct link to query?") ``` optional query?: string; ``` The initial query text to perform/display. *** ### theme?[​](#theme "Direct link to theme?") ``` optional theme?: Partial>; ``` A theme applied throughout the embedded widget. If a theme variant (e.g. dark) isn't supplied, the default Glean theme will be used when the user has applied that variant. If any color is not supplied for a variant, the default Glean color will be used. #### Inherited from[​](#inherited-from-16 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`theme`](/libraries/web-sdk/reference/interfaces/Options.md#theme) *** ### themeVariant?[​](#themevariant "Direct link to themeVariant?") ``` optional themeVariant?: ThemeVariantOrAuto; ``` Overrides the theme variant for the embedded widget (default: light) If 'auto' is selected, Glean will automatically switch between light and dark mode and remain in sync with the user device. #### Inherited from[​](#inherited-from-17 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`themeVariant`](/libraries/web-sdk/reference/interfaces/Options.md#themevariant) *** ### unauthorizedMessage?[​](#unauthorizedmessage "Direct link to unauthorizedMessage?") ``` optional unauthorizedMessage?: string; ``` A message to display when the current session does not have valid authorization. The message only applies when [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is used ([Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'). It can be used to instruct the user on how to get back to an authorized session, e,g, "Please refresh the page" or "Sign in to \[SSO provider]". #### Inherited from[​](#inherited-from-18 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`unauthorizedMessage`](/libraries/web-sdk/reference/interfaces/Options.md#unauthorizedmessage) *** ### urlsToOpenInCurrentTab?[​](#urlstoopenincurrenttab "Direct link to urlsToOpenInCurrentTab?") ``` optional urlsToOpenInCurrentTab?: string[]; ``` Always open matching URLs in the current tab Should be an array of strings representing a regular expression. For example: "^https\://example\\.com.\*$" will match any URL that starts with ''. Rules: * Every regular expression MUST start with '^' to ensure it matches from the start of the URL. * Regular expressions do not include search params when matching the URL. * A maximum of 10 entries are allowed in the array. #### Inherited from[​](#inherited-from-19 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`urlsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/Options.md#urlstoopenincurrenttab) *** ### webAppUrl?[​](#webappurl "Direct link to webAppUrl?") ``` optional webAppUrl?: string; ``` The web page URL where users access Glean. For example: https\://\.glean.com. Admins can find this value at in the `Web app URL` field. #### Inherited from[​](#inherited-from-20 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`webAppUrl`](/libraries/web-sdk/reference/interfaces/Options.md#webappurl) --- # SettingsOptions ## Extends[​](#extends "Direct link to Extends") * [`Options`](/libraries/web-sdk/reference/interfaces/Options.md) ## Properties[​](#properties "Direct link to Properties") ### authMethod?[​](#authmethod "Direct link to authMethod?") ``` optional authMethod?: "sso" | "token"; ``` The authentication method to use in the embedded widget. sso - Logged out users will see a login button in the embedded widget. Clicking the button will open the SSO login flow in a new window. token - The embedding page obtains an auth token on behalf of the user and passes it to the embedded widget. If this method is specified the [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) and [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) options must also be specified. #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`authMethod`](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) *** ### authToken?[​](#authtoken "Direct link to authToken?") ``` optional authToken?: AuthTokenDetails; ``` An authentication token for the user. Providing this obviates the need for the user to separately login to Glean. May be obtained via: If an authToken is provided, [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) must also be provided. #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`authToken`](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) *** ### backend?[​](#backend "Direct link to backend?") ``` optional backend?: string; ``` Overrides the search backend server to use. Setting this explicitly is always recommended as it alleviates the user from from having to manually enter their email address in order to route to the backend instance to log into. It's also useful for routing users to a production vs staging server (if available). Example: `https://{your}-be.glean.com/` #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`backend`](/libraries/web-sdk/reference/interfaces/Options.md#backend) *** ### disableAnalytics?[​](#disableanalytics "Direct link to disableAnalytics?") ``` optional disableAnalytics?: boolean; ``` If true, Glean will not collect client analytics or log activity for the embedded session. Analytics data is used to monitor and improve the product experience, so it's recommended to leave data collection on unless the user has specifically requested otherwise. #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`disableAnalytics`](/libraries/web-sdk/reference/interfaces/Options.md#disableanalytics) *** ### disableAssistant?[​](#disableassistant "Direct link to disableAssistant?") ``` optional disableAssistant?: boolean; ``` If true, Glean Assistant will not be enabled for the embedded session, including but not limited to generated answers, summaries, chat, and other features. #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`disableAssistant`](/libraries/web-sdk/reference/interfaces/Options.md#disableassistant) *** ### domainsToOpenInCurrentTab?[​](#domainstoopenincurrenttab "Direct link to domainsToOpenInCurrentTab?") ``` optional domainsToOpenInCurrentTab?: string[]; ``` Always open links from these domains in the current tab. Expects a list of one or more domain suffixes, e.g. `['example.com', 'foo.example.org']`. #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`domainsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/Options.md#domainstoopenincurrenttab) *** ### enable3PCookieAccessRequest?[​](#enable3pcookieaccessrequest "Direct link to enable3PCookieAccessRequest?") ``` optional enable3PCookieAccessRequest?: boolean; ``` Enable flow to request the user for access if third-party cookies are blocked. If true, the user will be prompted to allow cookie access for glean.com when they try to login. Default to true since api version >= 2025-06-19. Note: This flag will have no impact if third-party cookies are already allowed. Users will be able to login as usual. Refer to [Guide to third-party cookies in Glean](https://developers.glean.com/docs/browser_api/third_party_cookies/) for more details. #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`enable3PCookieAccessRequest`](/libraries/web-sdk/reference/interfaces/Options.md#enable3pcookieaccessrequest) *** ### enableActivityLogging?[​](#enableactivitylogging "Direct link to enableActivityLogging?") ``` optional enableActivityLogging?: boolean; ``` Logs an activity event for each URL change of the embedding page. This improves search quality when embedded within a datasource for which there are not other activity signals (e.g. Salesforce). Note: if disableAnalytics is true, activity logging will be disabled regardless of the value of this option. #### Inherited from[​](#inherited-from-7 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`enableActivityLogging`](/libraries/web-sdk/reference/interfaces/Options.md#enableactivitylogging) *** ### externalSessionId?[​](#externalsessionid "Direct link to externalSessionId?") ``` optional externalSessionId?: string; ``` An external session identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own session telemetry. #### Inherited from[​](#inherited-from-8 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`externalSessionId`](/libraries/web-sdk/reference/interfaces/Options.md#externalsessionid) *** ### externalUserId?[​](#externaluserid "Direct link to externalUserId?") ``` optional externalUserId?: string; ``` An external user identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own user telemetry. #### Inherited from[​](#inherited-from-9 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`externalUserId`](/libraries/web-sdk/reference/interfaces/Options.md#externaluserid) *** ### fontFaces?[​](#fontfaces "Direct link to fontFaces?") ``` optional fontFaces?: FontFaceDetails[]; ``` A list of custom font-faces to include in the embedded page. When provided, the custom font-faces can be selected via the fontFamily option. #### Inherited from[​](#inherited-from-10 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontFaces`](/libraries/web-sdk/reference/interfaces/Options.md#fontfaces) *** ### fontFamily?[​](#fontfamily "Direct link to fontFamily?") ``` optional fontFamily?: string; ``` The font-family used for text in the embedded widget. #### Inherited from[​](#inherited-from-11 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontFamily`](/libraries/web-sdk/reference/interfaces/Options.md#fontfamily) *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: "medium" | "large" | "larger"; ``` The base font size of the embedded app. Defaults to 'medium' which corresponds to the browser's default font size, typically 16px. The actual font sizes of elements are relative to this value, some will be larger or smaller. #### Inherited from[​](#inherited-from-12 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`fontSize`](/libraries/web-sdk/reference/interfaces/Options.md#fontsize) *** ### hideUnconfigurableDatasources?[​](#hideunconfigurabledatasources "Direct link to hideUnconfigurableDatasources?") ``` optional hideUnconfigurableDatasources?: boolean; ``` Controls whether a user sees admin-managed data sources that they cannot configure. *** ### key?[​](#key "Direct link to key?") ``` optional key?: string; ``` Uniquely identify the widget. Must be used if more than one Glean widget of the same type are rendered on the page Allowed characters: \[a-z A-Z - \_ . ; :] #### Inherited from[​](#inherited-from-13 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`key`](/libraries/web-sdk/reference/interfaces/Options.md#key) *** ### locale?[​](#locale "Direct link to locale?") ``` optional locale?: string; ``` Overrides the default locale for the embedded widget users. If 'auto' (default) is used, Glean will automatically detect the locale from user's browser. If an unsupported locale is provided, Glean will fallback to use the closest supported locale. If user picked their locale for Glean, their choice will be respected. #### Inherited from[​](#inherited-from-14 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`locale`](/libraries/web-sdk/reference/interfaces/Options.md#locale) *** ### onAuthTokenRequired?[​](#onauthtokenrequired "Direct link to onAuthTokenRequired?") ``` optional onAuthTokenRequired?: | (() => Promise) | (() => void); ``` A callback invoked when the user requires a new authentication token. Only invoked if an [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is provided initially and is nearing expiration. This option is required when [Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'. **Signatures:** 1. `() => Promise` - (Recommended) Return a Promise that resolves to the new token details. The SDK will update the token internally without requiring a re-render. #### Example[​](#example "Direct link to Example") ``` onAuthTokenRequired: async () => { const token = await fetchNewToken() return { token: token.value, expirationTime: token.expiresAt } } 2. `() => void` - (Legacy) The callback must fetch a new token and call the SDK's render method with updated options containing the new token. ``` #### Inherited from[​](#inherited-from-15 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`onAuthTokenRequired`](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) *** ### theme?[​](#theme "Direct link to theme?") ``` optional theme?: Partial>; ``` A theme applied throughout the embedded widget. If a theme variant (e.g. dark) isn't supplied, the default Glean theme will be used when the user has applied that variant. If any color is not supplied for a variant, the default Glean color will be used. #### Inherited from[​](#inherited-from-16 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`theme`](/libraries/web-sdk/reference/interfaces/Options.md#theme) *** ### themeVariant?[​](#themevariant "Direct link to themeVariant?") ``` optional themeVariant?: ThemeVariantOrAuto; ``` Overrides the theme variant for the embedded widget (default: light) If 'auto' is selected, Glean will automatically switch between light and dark mode and remain in sync with the user device. #### Inherited from[​](#inherited-from-17 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`themeVariant`](/libraries/web-sdk/reference/interfaces/Options.md#themevariant) *** ### unauthorizedMessage?[​](#unauthorizedmessage "Direct link to unauthorizedMessage?") ``` optional unauthorizedMessage?: string; ``` A message to display when the current session does not have valid authorization. The message only applies when [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is used ([Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'). It can be used to instruct the user on how to get back to an authorized session, e,g, "Please refresh the page" or "Sign in to \[SSO provider]". #### Inherited from[​](#inherited-from-18 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`unauthorizedMessage`](/libraries/web-sdk/reference/interfaces/Options.md#unauthorizedmessage) *** ### urlsToOpenInCurrentTab?[​](#urlstoopenincurrenttab "Direct link to urlsToOpenInCurrentTab?") ``` optional urlsToOpenInCurrentTab?: string[]; ``` Always open matching URLs in the current tab Should be an array of strings representing a regular expression. For example: "^https\://example\\.com.\*$" will match any URL that starts with ''. Rules: * Every regular expression MUST start with '^' to ensure it matches from the start of the URL. * Regular expressions do not include search params when matching the URL. * A maximum of 10 entries are allowed in the array. #### Inherited from[​](#inherited-from-19 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`urlsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/Options.md#urlstoopenincurrenttab) *** ### webAppUrl?[​](#webappurl "Direct link to webAppUrl?") ``` optional webAppUrl?: string; ``` The web page URL where users access Glean. For example: https\://\.glean.com. Admins can find this value at in the `Web app URL` field. #### Inherited from[​](#inherited-from-20 "Direct link to Inherited from") [`Options`](/libraries/web-sdk/reference/interfaces/Options.md).[`webAppUrl`](/libraries/web-sdk/reference/interfaces/Options.md#webappurl) --- # TabbedSearchOptions ## Extends[​](#extends "Direct link to Extends") * [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md) ## Properties[​](#properties "Direct link to Properties") ### authMethod?[​](#authmethod "Direct link to authMethod?") ``` optional authMethod?: "sso" | "token"; ``` The authentication method to use in the embedded widget. sso - Logged out users will see a login button in the embedded widget. Clicking the button will open the SSO login flow in a new window. token - The embedding page obtains an auth token on behalf of the user and passes it to the embedded widget. If this method is specified the [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) and [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) options must also be specified. #### Inherited from[​](#inherited-from "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`authMethod`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#authmethod) *** ### authToken?[​](#authtoken "Direct link to authToken?") ``` optional authToken?: AuthTokenDetails; ``` An authentication token for the user. Providing this obviates the need for the user to separately login to Glean. May be obtained via: If an authToken is provided, [Options.onAuthTokenRequired](/libraries/web-sdk/reference/interfaces/Options.md#onauthtokenrequired) must also be provided. #### Inherited from[​](#inherited-from-1 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`authToken`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#authtoken) *** ### autoExpand?[​](#autoexpand "Direct link to autoExpand?") ``` optional autoExpand?: boolean; ``` If true, the search results frame will automatically expand its height to match the content, removing the need for an internal scrollbar. The embedder is responsible for providing a scrollable container around the widget. *** ### backend?[​](#backend "Direct link to backend?") ``` optional backend?: string; ``` Overrides the search backend server to use. Setting this explicitly is always recommended as it alleviates the user from from having to manually enter their email address in order to route to the backend instance to log into. It's also useful for routing users to a production vs staging server (if available). Example: `https://{your}-be.glean.com/` #### Inherited from[​](#inherited-from-2 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`backend`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#backend) *** ### datasource?[​](#datasource "Direct link to datasource?") ``` optional datasource?: string; ``` Select the search tab for the given datasource (e.g. `'confluence'`, `'gdrive'`, `'people'`) instead of the all tab. Other datasource tabs will be shown if results are available for the current query. See also [SearchOptions.datasourcesFilter](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter). #### Inherited from[​](#inherited-from-3 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasource`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource) *** ### datasourcesFilter?[​](#datasourcesfilter "Direct link to datasourcesFilter?") ``` optional datasourcesFilter?: string[]; ``` Filter search results to only the given list of datasources (e.g. `['figma']`, `['jira', 'zendesk']`). Results from other datasources will not be available. See also [SearchOptions.datasource](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasource). #### Inherited from[​](#inherited-from-4 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`datasourcesFilter`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#datasourcesfilter) *** ### defaultResultTabs?[​](#defaultresulttabs "Direct link to defaultResultTabs?") ``` optional defaultResultTabs?: string[]; ``` A list of result tabs to show by default. Other result tabs (if available) will be visible via the filter selector button. *** ### disableAnalytics?[​](#disableanalytics "Direct link to disableAnalytics?") ``` optional disableAnalytics?: boolean; ``` If true, Glean will not collect client analytics or log activity for the embedded session. Analytics data is used to monitor and improve the product experience, so it's recommended to leave data collection on unless the user has specifically requested otherwise. #### Inherited from[​](#inherited-from-5 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`disableAnalytics`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#disableanalytics) *** ### disableAssistant?[​](#disableassistant "Direct link to disableAssistant?") ``` optional disableAssistant?: boolean; ``` If true, Glean Assistant will not be enabled for the embedded session, including but not limited to generated answers, summaries, chat, and other features. #### Inherited from[​](#inherited-from-6 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`disableAssistant`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#disableassistant) *** ### domainsToOpenInCurrentTab?[​](#domainstoopenincurrenttab "Direct link to domainsToOpenInCurrentTab?") ``` optional domainsToOpenInCurrentTab?: string[]; ``` Always open links from these domains in the current tab. Expects a list of one or more domain suffixes, e.g. `['example.com', 'foo.example.org']`. #### Inherited from[​](#inherited-from-7 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`domainsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#domainstoopenincurrenttab) *** ### enable3PCookieAccessRequest?[​](#enable3pcookieaccessrequest "Direct link to enable3PCookieAccessRequest?") ``` optional enable3PCookieAccessRequest?: boolean; ``` Enable flow to request the user for access if third-party cookies are blocked. If true, the user will be prompted to allow cookie access for glean.com when they try to login. Default to true since api version >= 2025-06-19. Note: This flag will have no impact if third-party cookies are already allowed. Users will be able to login as usual. Refer to [Guide to third-party cookies in Glean](https://developers.glean.com/docs/browser_api/third_party_cookies/) for more details. #### Inherited from[​](#inherited-from-8 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`enable3PCookieAccessRequest`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#enable3pcookieaccessrequest) *** ### enableActivityLogging?[​](#enableactivitylogging "Direct link to enableActivityLogging?") ``` optional enableActivityLogging?: boolean; ``` Logs an activity event for each URL change of the embedding page. This improves search quality when embedded within a datasource for which there are not other activity signals (e.g. Salesforce). Note: if disableAnalytics is true, activity logging will be disabled regardless of the value of this option. #### Inherited from[​](#inherited-from-9 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`enableActivityLogging`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#enableactivitylogging) *** ### externalSessionId?[​](#externalsessionid "Direct link to externalSessionId?") ``` optional externalSessionId?: string; ``` An external session identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own session telemetry. #### Inherited from[​](#inherited-from-10 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`externalSessionId`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#externalsessionid) *** ### externalUserId?[​](#externaluserid "Direct link to externalUserId?") ``` optional externalUserId?: string; ``` An external user identifier provided by the embedder. Included in all analytics events so embedders can join Glean logs with their own user telemetry. #### Inherited from[​](#inherited-from-11 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`externalUserId`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#externaluserid) *** ### filters?[​](#filters "Direct link to filters?") ``` optional filters?: FilterValue[]; ``` Filters applied on all searches done by the user. Any filters in this list will override any manually input filters from users or filters defined in [initialFilters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) that share the same key. These filters are non-modifiable by the user, and will not show up in the UI as being applied. For example, if a filter with some key `keyA` is specified here, then: * Users will not be able to modify any filters corresponding to `keyA` in the UI in any way * The filter dropdown for that key will not show up on the UI (nor in the overflow button) * Search bar operators (i.e. `keyA:[value]`) with that key will be overridden and removed from the search bar query * Any filters defined in [initialFilters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) with `keyA` will be ignored. When specifying the list of filters the same `key` may be specified more than once, in which case the `value`s for that key are combined via an 'OR' operation. The `value`s for different `key`s are combined via 'AND'. For example, this set of filters will match all results that were updated in the past week AND are of type "bug" OR "task": ``` [{ key: "type", value: "bug" }, { key: "type", value: "task" }, { key: "updated", value: "past_week" }] ``` #### Inherited from[​](#inherited-from-12 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`filters`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#filters) *** ### fontFaces?[​](#fontfaces "Direct link to fontFaces?") ``` optional fontFaces?: FontFaceDetails[]; ``` A list of custom font-faces to include in the embedded page. When provided, the custom font-faces can be selected via the fontFamily option. #### Inherited from[​](#inherited-from-13 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontFaces`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontfaces) *** ### fontFamily?[​](#fontfamily "Direct link to fontFamily?") ``` optional fontFamily?: string; ``` The font-family used for text in the embedded widget. #### Inherited from[​](#inherited-from-14 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontFamily`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontfamily) *** ### fontSize?[​](#fontsize "Direct link to fontSize?") ``` optional fontSize?: "medium" | "large" | "larger"; ``` The base font size of the embedded app. Defaults to 'medium' which corresponds to the browser's default font size, typically 16px. The actual font sizes of elements are relative to this value, some will be larger or smaller. #### Inherited from[​](#inherited-from-15 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`fontSize`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#fontsize) *** ### hideAutocomplete?[​](#hideautocomplete "Direct link to hideAutocomplete?") ``` optional hideAutocomplete?: boolean; ``` If true, search boxes will not render the autocomplete dropdown. #### Inherited from[​](#inherited-from-16 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`hideAutocomplete`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#hideautocomplete) *** ### hideDatasourceFilter?[​](#hidedatasourcefilter "Direct link to hideDatasourceFilter?") ``` optional hideDatasourceFilter?: boolean; ``` If true, hide the datasource filter in the righthand column of the search results page. *** ### hideFiltersColumn?[​](#hidefilterscolumn "Direct link to hideFiltersColumn?") ``` optional hideFiltersColumn?: boolean; ``` If true, hides the righthand column of the search results page. *** ### hideTopBarFilters?[​](#hidetopbarfilters "Direct link to hideTopBarFilters?") ``` optional hideTopBarFilters?: boolean; ``` If true, hide the top bar filters in the search results page. *** ### ~~initialFilters?~~[​](#initialfilters "Direct link to initialfilters") ``` optional initialFilters?: FilterValue[]; ``` #### Deprecated[​](#deprecated "Direct link to Deprecated") Use [filters](/libraries/web-sdk/reference/interfaces/SearchOptions.md#filters) instead. #### Inherited from[​](#inherited-from-17 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`initialFilters`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#initialfilters) *** ### key?[​](#key "Direct link to key?") ``` optional key?: string; ``` Uniquely identify the widget. Must be used if more than one Glean widget of the same type are rendered on the page Allowed characters: \[a-z A-Z - \_ . ; :] #### Inherited from[​](#inherited-from-18 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`key`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#key) *** ### locale?[​](#locale "Direct link to locale?") ``` optional locale?: string; ``` Overrides the default locale for the embedded widget users. If 'auto' (default) is used, Glean will automatically detect the locale from user's browser. If an unsupported locale is provided, Glean will fallback to use the closest supported locale. If user picked their locale for Glean, their choice will be respected. #### Inherited from[​](#inherited-from-19 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`locale`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#locale) *** ### onAuthTokenRequired?[​](#onauthtokenrequired "Direct link to onAuthTokenRequired?") ``` optional onAuthTokenRequired?: | (() => Promise) | (() => void); ``` A callback invoked when the user requires a new authentication token. Only invoked if an [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is provided initially and is nearing expiration. This option is required when [Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'. **Signatures:** 1. `() => Promise` - (Recommended) Return a Promise that resolves to the new token details. The SDK will update the token internally without requiring a re-render. #### Example[​](#example "Direct link to Example") ``` onAuthTokenRequired: async () => { const token = await fetchNewToken() return { token: token.value, expirationTime: token.expiresAt } } 2. `() => void` - (Legacy) The callback must fetch a new token and call the SDK's render method with updated options containing the new token. ``` #### Inherited from[​](#inherited-from-20 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onAuthTokenRequired`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onauthtokenrequired) *** ### onChat?[​](#onchat "Direct link to onChat?") ``` optional onChat?: (chatId?) => void; ``` A callback invoked when the user tries to initiate a chat from search pages, like follow ups from AI Answer. If provided, we will allow and show follow ups on AI Answer for user to continue to chat from there, typical usage can be: 1. open the chat in a new page 2. or open chat with `renderChat` method on the same page #### Parameters[​](#parameters "Direct link to Parameters") | Parameter | Type | Description | | --------- | -------- | -------------------------------------------------------------------------------------- | | `chatId?` | `string` | An opaque id that stores the context of the current / new chat for the current session | #### Returns[​](#returns "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-21 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onChat`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onchat) *** ### onDatasourceChange?[​](#ondatasourcechange "Direct link to onDatasourceChange?") ``` optional onDatasourceChange?: (datasource?) => void; ``` A callback invoked when the user changes search tabs to a different datasource. If `undefined`, all datasources are shown. #### Parameters[​](#parameters-1 "Direct link to Parameters") | Parameter | Type | | ------------- | -------- | | `datasource?` | `string` | #### Returns[​](#returns-1 "Direct link to Returns") `void` *** ### onSearch[​](#onsearch "Direct link to onSearch") ``` onSearch: (query) => void; ``` A callback invoked when the user performs a search query. #### Parameters[​](#parameters-2 "Direct link to Parameters") | Parameter | Type | | --------- | -------- | | `query` | `string` | #### Returns[​](#returns-2 "Direct link to Returns") `void` #### Inherited from[​](#inherited-from-22 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`onSearch`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#onsearch) *** ### query?[​](#query "Direct link to query?") ``` optional query?: string; ``` The initial query text to perform/display. #### Inherited from[​](#inherited-from-23 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`query`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#query) *** ### searchBoxCustomizations?[​](#searchboxcustomizations "Direct link to searchBoxCustomizations?") ``` optional searchBoxCustomizations?: SearchBoxCustomizations; ``` Optional style customizations for the search box, only used when `showInlineSearchBox` is true. *** ### showAutocompleteContent?[​](#showautocompletecontent "Direct link to showAutocompleteContent?") ``` optional showAutocompleteContent?: boolean; ``` When the inline search box is visible, this option allows displaying autocomplete content in place of search results when the search query is empty. Note that this option is mutually exclusive with 'showHomePageContent', and if both are set, 'showHomePageContent' takes precedence. *** ### showHomePageContent?[​](#showhomepagecontent "Direct link to showHomePageContent?") ``` optional showHomePageContent?: boolean; ``` Whether to show home page content in place of search results when the search query is empty. *** ### showInlineSearchBox?[​](#showinlinesearchbox "Direct link to showInlineSearchBox?") ``` optional showInlineSearchBox?: boolean; ``` Whether or not to show the search box inline above the search results. *** ### theme?[​](#theme "Direct link to theme?") ``` optional theme?: Partial>; ``` A theme applied throughout the embedded widget. If a theme variant (e.g. dark) isn't supplied, the default Glean theme will be used when the user has applied that variant. If any color is not supplied for a variant, the default Glean color will be used. #### Inherited from[​](#inherited-from-24 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`theme`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#theme) *** ### themeVariant?[​](#themevariant "Direct link to themeVariant?") ``` optional themeVariant?: ThemeVariantOrAuto; ``` Overrides the theme variant for the embedded widget (default: light) If 'auto' is selected, Glean will automatically switch between light and dark mode and remain in sync with the user device. #### Inherited from[​](#inherited-from-25 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`themeVariant`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#themevariant) *** ### topBarFilterOverrides?[​](#topbarfilteroverrides "Direct link to topBarFilterOverrides?") ``` optional topBarFilterOverrides?: FilterConfig[]; ``` If specified, these filters will be displayed in the top bar above the results pane instead of the default filters, in the order specified. *** ### unauthorizedMessage?[​](#unauthorizedmessage "Direct link to unauthorizedMessage?") ``` optional unauthorizedMessage?: string; ``` A message to display when the current session does not have valid authorization. The message only applies when [Options.authToken](/libraries/web-sdk/reference/interfaces/Options.md#authtoken) is used ([Options.authMethod](/libraries/web-sdk/reference/interfaces/Options.md#authmethod) is 'token'). It can be used to instruct the user on how to get back to an authorized session, e,g, "Please refresh the page" or "Sign in to \[SSO provider]". #### Inherited from[​](#inherited-from-26 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`unauthorizedMessage`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#unauthorizedmessage) *** ### urlsToOpenInCurrentTab?[​](#urlstoopenincurrenttab "Direct link to urlsToOpenInCurrentTab?") ``` optional urlsToOpenInCurrentTab?: string[]; ``` Always open matching URLs in the current tab Should be an array of strings representing a regular expression. For example: "^https\://example\\.com.\*$" will match any URL that starts with ''. Rules: * Every regular expression MUST start with '^' to ensure it matches from the start of the URL. * Regular expressions do not include search params when matching the URL. * A maximum of 10 entries are allowed in the array. #### Inherited from[​](#inherited-from-27 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`urlsToOpenInCurrentTab`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#urlstoopenincurrenttab) *** ### webAppUrl?[​](#webappurl "Direct link to webAppUrl?") ``` optional webAppUrl?: string; ``` The web page URL where users access Glean. For example: https\://\.glean.com. Admins can find this value at in the `Web app URL` field. #### Inherited from[​](#inherited-from-28 "Direct link to Inherited from") [`SearchOptions`](/libraries/web-sdk/reference/interfaces/SearchOptions.md).[`webAppUrl`](/libraries/web-sdk/reference/interfaces/SearchOptions.md#webappurl) --- # Theme ## Properties[​](#properties "Direct link to Properties") ### background?[​](#background "Direct link to background?") ``` optional background?: string; ``` The primary page background color. *** ### backgroundLight?[​](#backgroundlight "Direct link to backgroundLight?") ``` optional backgroundLight?: string; ``` The background color for content that is lightly promoted and sits one level higher than `backgroundMid`. *** ### backgroundMid?[​](#backgroundmid "Direct link to backgroundMid?") ``` optional backgroundMid?: string; ``` The background color for cards and other content that sits one level higher than `background`. *** ### ~~backgroundSuccess?~~[​](#backgroundsuccess "Direct link to backgroundsuccess") ``` optional backgroundSuccess?: string; ``` #### Deprecated[​](#deprecated "Direct link to Deprecated") use successBackground instead. The background color for success content. Must have sufficient contrast with textPrimary. *** ### bannerPrimaryBackground?[​](#bannerprimarybackground "Direct link to bannerPrimaryBackground?") ``` optional bannerPrimaryBackground?: string; ``` The background color used for primary banners *** ### bannerSecondaryBackground?[​](#bannersecondarybackground "Direct link to bannerSecondaryBackground?") ``` optional bannerSecondaryBackground?: string; ``` The background color used for secondary banners *** ### borderDark?[​](#borderdark "Direct link to borderDark?") ``` optional borderDark?: string; ``` The border colors for cards and form elements that should be strongly distinguished from their surroundings. Should be discernable from all background colors. *** ### borderLight?[​](#borderlight "Direct link to borderLight?") ``` optional borderLight?: string; ``` The border colors for cards and form elements that should be subtly distinguished from their surroundings. Should be discernable from all background colors. *** ### chatBackground?[​](#chatbackground "Direct link to chatBackground?") ``` optional chatBackground?: string; ``` The background color for chat. Should be a hex color code. *** ### companyBackgroundTextPrimary?[​](#companybackgroundtextprimary "Direct link to companyBackgroundTextPrimary?") ``` optional companyBackgroundTextPrimary?: string; ``` The color for prominent text on the company provided background image. Should have sufficient contrast with the company background image. *** ### disabled?[​](#disabled "Direct link to disabled?") ``` optional disabled?: string; ``` The color for disabled elements *** ### disabledBackground?[​](#disabledbackground "Direct link to disabledBackground?") ``` optional disabledBackground?: string; ``` Background color for disabled elements *** ### disabledHighlight?[​](#disabledhighlight "Direct link to disabledHighlight?") ``` optional disabledHighlight?: string; ``` The color for disabled elements *** ### error?[​](#error "Direct link to error?") ``` optional error?: string; ``` The color that is used for text and icons that indicate an error condition. Should have sufficient contrast with all background colors. *** ### errorBackground?[​](#errorbackground "Direct link to errorBackground?") ``` optional errorBackground?: string; ``` The background color for error content. Must have sufficient contrast with textPrimary. *** ### hover?[​](#hover "Direct link to hover?") ``` optional hover?: string; ``` The background color used upon hovering over interactable elements such as buttons. *** ### info?[​](#info "Direct link to info?") ``` optional info?: string; ``` The background color used for info content. *** ### infoBackground?[​](#infobackground "Direct link to infoBackground?") ``` optional infoBackground?: string; ``` The background color for info content. *** ### infoForeground?[​](#infoforeground "Direct link to infoForeground?") ``` optional infoForeground?: string; ``` The foreground color for info content. *** ### navBarBackground?[​](#navbarbackground "Direct link to navBarBackground?") ``` optional navBarBackground?: string; ``` The background color for the app-wide navigation bar. *** ### navBarForeground?[​](#navbarforeground "Direct link to navBarForeground?") ``` optional navBarForeground?: string; ``` The foreground icon color for the app-wide navigation bar. Should have sufficient contrast with `navBarBackground`. *** ### navigationInPageBackground?[​](#navigationinpagebackground "Direct link to navigationInPageBackground?") ``` optional navigationInPageBackground?: string; ``` The background color for the app-wide in-page menu. *** ### primaryHighlight?[​](#primaryhighlight "Direct link to primaryHighlight?") ``` optional primaryHighlight?: string; ``` The primary accent color used for hyperlinks, buttons, and overall branding. Should have sufficient contrast with all background colors. *** ### primaryHover?[​](#primaryhover "Direct link to primaryHover?") ``` optional primaryHover?: string; ``` The hover color for interactable `primaryHighlight`. Should have sufficient contrast with all background colors. *** ### selected?[​](#selected "Direct link to selected?") ``` optional selected?: string; ``` The background color used when an interactable element such as a menu option or toggle is actively applied. *** ### shellBackground?[​](#shellbackground "Direct link to shellBackground?") ``` optional shellBackground?: string; ``` The background for the app shell. This can be hex value, linear-gradient or image (url). *** ### success?[​](#success "Direct link to success?") ``` optional success?: string; ``` The color that is used for text and icons that indicate an success condition. Should have sufficient contrast with all background colors. *** ### successBackground?[​](#successbackground "Direct link to successBackground?") ``` optional successBackground?: string; ``` The background color used to indicate successes. For ex, in: banners, toasts, etc. *** ### textHighlight?[​](#texthighlight "Direct link to textHighlight?") ``` optional textHighlight?: string; ``` The color for text that is highlighted. Should have sufficient contrast with all background colors. *** ### textPrimary?[​](#textprimary "Direct link to textPrimary?") ``` optional textPrimary?: string; ``` The color for prominent text. Should have sufficient contrast with all background colors. *** ### textSecondary?[​](#textsecondary "Direct link to textSecondary?") ``` optional textSecondary?: string; ``` The color for secondary text. Should have sufficient contrast with all background colors, but less than `textPrimary`. *** ### visited?[​](#visited "Direct link to visited?") ``` optional visited?: string; ``` The color for visited hyperlinks. *** ### warning?[​](#warning "Direct link to warning?") ``` optional warning?: string; ``` The color that is used for distinguishing warning elements from their surroundings *** ### warningBackground?[​](#warningbackground "Direct link to warningBackground?") ``` optional warningBackground?: string; ``` The background color for warning content. Must have sufficient contrast with textPrimary. *** ### warningDark?[​](#warningdark "Direct link to warningDark?") ``` optional warningDark?: string; ``` The color that is used for strongly distinguishing warning elements from their surroundings *** ### warningLight?[​](#warninglight "Direct link to warningLight?") ``` optional warningLight?: string; ``` The color that is used for subtly distinguishing warning elements from their surroundings *** ### warningText?[​](#warningtext "Direct link to warningText?") ``` optional warningText?: string; ``` The color that is used for distinguishing warning text from their surroundings --- # WidgetHandle ## Properties[​](#properties "Direct link to Properties") ### focus[​](#focus "Direct link to focus") ``` focus: () => void; ``` Focus on the attached Glean widget #### Returns[​](#returns "Direct link to Returns") `void` --- # ThemeVariant ``` type ThemeVariant = "light" | "dark"; ``` --- # WebSdkChatEvent ``` type WebSdkChatEvent = | { name: "chat:page_view"; } | { chatId: string | undefined; name: "chat:id_update"; } | { id?: string; name: "chat:location_update"; type: ChatLocation; } | { name: "chat:action_click"; payload: string; }; ``` Chat events that can be emitted by the chat component * chat:id\_update and chat:location\_update events may be sent multiple times for the same ID and the handler should be idempotent ## Union Members[​](#union-members "Direct link to Union Members") ### Type Literal[​](#type-literal "Direct link to Type Literal") ``` { name: "chat:page_view"; } ``` *** ### Type Literal[​](#type-literal-1 "Direct link to Type Literal") ``` { chatId: string | undefined; name: "chat:id_update"; } ``` | Name | Type | Description | | -------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `chatId` | `string` \| `undefined` | chatId can be undefined if the chatId is not yet available in current chat session, for example when the current chat is cleared. | | `name` | `"chat:id_update"` | Deprecated: Use chat:location\_update instead | *** ### Type Literal[​](#type-literal-2 "Direct link to Type Literal") ``` { id?: string; name: "chat:location_update"; type: ChatLocation; } ``` | Name | Type | Description | | ------ | ------------------------ | --------------------------------------------------------------- | | `id?` | `string` | Can be undefined, for example when the current chat is cleared. | | `name` | `"chat:location_update"` | - | | `type` | `ChatLocation` | - | *** ### Type Literal[​](#type-literal-3 "Direct link to Type Literal") ``` { name: "chat:action_click"; payload: string; } ``` --- # attach ``` const attach: GleanWebSDK["attach"]; ``` --- # createGuestAuthProvider ``` const createGuestAuthProvider: GleanWebSDK["createGuestAuthProvider"]; ``` --- # default ``` const default: GleanWebSDK; ``` --- # getChatOptionsFromUrl ``` const getChatOptionsFromUrl: GleanWebSDK["getChatOptionsFromUrl"]; ``` --- # openSidebar ``` const openSidebar: GleanWebSDK["openSidebar"]; ``` --- # renderChat ``` const renderChat: GleanWebSDK["renderChat"]; ``` --- # renderRecommendations ``` const renderRecommendations: GleanWebSDK["renderRecommendations"]; ``` --- # renderSearchBox ``` const renderSearchBox: GleanWebSDK["renderSearchBox"]; ``` --- # renderSearchResults ``` const renderSearchResults: GleanWebSDK["renderSearchResults"]; ``` --- # version ``` const version: string; ``` ---