The Widgets API is a GraphQL API that allows you to build custom user interfaces for enterprise application workflows using WorkOS tools and services, giving you full control over the user experience.
Unlike the WorkOS REST API, the Widgets API can be called directly from the browser on behalf of a signed-in user. Every request is authorized by a short-lived session token, so a user can only ever read and write what they are entitled to based on their role and permissions. All operations are sent as POST requests to a single endpoint.
Send the query and its variables as a JSON body, with the Widget token in the Authorization header.
| curl --request POST \ | |
| --url "https://api.workos.com/client/graphql" \ | |
| --header "Authorization: Bearer <Widget token>" \ | |
| --header "Content-Type: application/json" \ | |
| -d @- <<'BODY' | |
| { | |
| "query": "query Organization($id: ID!) { organization(id: $id) { id name } }", | |
| "variables": { "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" } | |
| } | |
| BODY |
| const response = await fetch('https://api.workos.com/client/graphql', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': 'Bearer <Widget token>', | |
| }, | |
| body: JSON.stringify({ | |
| query: 'query Organization($id: ID!) { organization(id: $id) { id name } }', | |
| variables: { | |
| id: 'org_01EHZNVPK3SFK441A1RGBFSHRT', | |
| }, | |
| }), | |
| }); |
| require "json" | |
| require "net/http" | |
| uri = URI("https://api.workos.com/client/graphql") | |
| request = Net::HTTP::Post.new(uri) | |
| request["Content-Type"] = "application/json" | |
| request["Authorization"] = "Bearer <Widget token>" | |
| request.body = JSON.generate( | |
| query: "query Organization($id: ID!) { organization(id: $id) { id name } }", | |
| variables: { id: "org_01EHZNVPK3SFK441A1RGBFSHRT" } | |
| ) | |
| response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| | |
| http.request(request) | |
| end |
| import requests | |
| response = requests.post( | |
| "https://api.workos.com/client/graphql", | |
| headers={ | |
| "Content-Type": "application/json", | |
| "Authorization": "Bearer <Widget token>", | |
| }, | |
| json={ | |
| "query": "query Organization($id: ID!) { organization(id: $id) { id name } }", | |
| "variables": {"id": "org_01EHZNVPK3SFK441A1RGBFSHRT"}, | |
| }, | |
| ) |
| package main | |
| import ( | |
| "net/http" | |
| "strings" | |
| ) | |
| func main() { | |
| body := strings.NewReader(`{ | |
| "query": "query Organization($id: ID!) { organization(id: $id) { id name } }", | |
| "variables": { "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" } | |
| }`) | |
| request, err := http.NewRequest(http.MethodPost, "https://api.workos.com/client/graphql", body) | |
| if err != nil { | |
| // Handle the error... | |
| } | |
| request.Header.Set("Content-Type", "application/json") | |
| request.Header.Set("Authorization", "Bearer <Widget token>") | |
| response, err := http.DefaultClient.Do(request) | |
| if err != nil { | |
| // Handle the error... | |
| } | |
| defer response.Body.Close() | |
| } |
| <?php | |
| $body = json_encode([ | |
| "query" => | |
| 'query Organization($id: ID!) { organization(id: $id) { id name } }', | |
| "variables" => ["id" => "org_01EHZNVPK3SFK441A1RGBFSHRT"], | |
| ]); | |
| $curl = curl_init("https://api.workos.com/client/graphql"); | |
| curl_setopt_array($curl, [ | |
| CURLOPT_POST => true, | |
| CURLOPT_RETURNTRANSFER => true, | |
| CURLOPT_HTTPHEADER => [ | |
| "Content-Type: application/json", | |
| "Authorization: Bearer <Widget token>", | |
| ], | |
| CURLOPT_POSTFIELDS => $body, | |
| ]); | |
| $response = curl_exec($curl); | |
| curl_close($curl); |
| import java.net.URI; | |
| import java.net.http.HttpClient; | |
| import java.net.http.HttpRequest; | |
| import java.net.http.HttpResponse; | |
| String body = """ | |
| { | |
| "query": "query Organization($id: ID!) { organization(id: $id) { id name } }", | |
| "variables": { "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" } | |
| } | |
| """; | |
| HttpRequest request = | |
| HttpRequest.newBuilder() | |
| .uri(URI.create("https://api.workos.com/client/graphql")) | |
| .header("Content-Type", "application/json") | |
| .header("Authorization", "Bearer <Widget token>") | |
| .POST(HttpRequest.BodyPublishers.ofString(body)) | |
| .build(); | |
| HttpResponse<String> response = | |
| HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); |
| using System.Net.Http; | |
| using System.Text; | |
| using System.Text.Json; | |
| var body = JsonSerializer.Serialize(new { | |
| query = "query Organization($id: ID!) { organization(id: $id) { id name } }", | |
| variables = new { id = "org_01EHZNVPK3SFK441A1RGBFSHRT" }, | |
| }); | |
| var request = new HttpRequestMessage(HttpMethod.Post, "https://api.workos.com/client/graphql") { | |
| Content = new StringContent(body, Encoding.UTF8, "application/json"), | |
| }; | |
| request.Headers.Add("Authorization", "Bearer <Widget token>"); | |
| var response = await new HttpClient().SendAsync(request); |
| use serde_json::json; | |
| #[tokio::main] | |
| async fn main() -> Result<(), reqwest::Error> { | |
| let _response = reqwest::Client::new() | |
| .post("https://api.workos.com/client/graphql") | |
| .header("Content-Type", "application/json") | |
| .header("Authorization", "Bearer <Widget token>") | |
| .json(&json!({ | |
| "query": "query Organization($id: ID!) { organization(id: $id) { id name } }", | |
| "variables": { "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" } | |
| })) | |
| .send() | |
| .await?; | |
| Ok(()) | |
| } |
| { | |
| "data": { | |
| "organization": { | |
| "id": "org_01EHZNVPK3SFK441A1RGBFSHRT", | |
| "name": "Foo Corp" | |
| } | |
| } | |
| } |
The Widgets API supports automatic persisted queries so clients can send the SHA256 hash of an operation in place of its full text. These queries are cached to decrease the payload delivered across the network.
Automatic persisted queries are not enabled by default. Contact support to have them enabled for your account.
To register an operation, send its text together with the hash in extensions.persistedQuery. The hash is the SHA256 digest of the exact query string, and version must be 1. If the hash does not match the query, the response is an error with the code PERSISTED_QUERY_MISMATCH and an HTTP 400 status, and nothing is registered.
| curl --request POST \ | |
| --url "https://api.workos.com/client/graphql" \ | |
| --header "Authorization: Bearer <Widget token>" \ | |
| --header "Content-Type: application/json" \ | |
| -d @- <<'BODY' | |
| { | |
| "query": "query Organization($id: ID!) { organization(id: $id) { id name } }", | |
| "variables": { "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" }, | |
| "extensions": { | |
| "persistedQuery": { | |
| "version": 1, | |
| "sha256Hash": "4d5423a77671dba429e5b7d5a1abe135d89ac5df85793692987111664ab20070" | |
| } | |
| } | |
| } | |
| BODY |
| { | |
| "errors": [ | |
| { | |
| "message": "The provided `sha256Hash` does not match the SHA256 hash of `query`.", | |
| "extensions": { | |
| "code": "PERSISTED_QUERY_MISMATCH" | |
| } | |
| } | |
| ], | |
| "data": null | |
| } |
Later requests for the same operation can omit query and send only the hash and variables. Queries and mutations are both supported.
| curl --request POST \ | |
| --url "https://api.workos.com/client/graphql" \ | |
| --header "Authorization: Bearer <Widget token>" \ | |
| --header "Content-Type: application/json" \ | |
| -d @- <<'BODY' | |
| { | |
| "variables": { "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" }, | |
| "extensions": { | |
| "persistedQuery": { | |
| "version": 1, | |
| "sha256Hash": "4d5423a77671dba429e5b7d5a1abe135d89ac5df85793692987111664ab20070" | |
| } | |
| } | |
| } | |
| BODY |
Registrations expire seven days after they are made. When a hash is not registered, the response is an error with the code PERSISTED_QUERY_NOT_FOUND and an HTTP 200 status. Retry the request with the query included to register it again.
| { | |
| "errors": [ | |
| { | |
| "message": "PersistedQueryNotFound", | |
| "extensions": { | |
| "code": "PERSISTED_QUERY_NOT_FOUND" | |
| } | |
| } | |
| ] | |
| } |
GraphQL clients such as Apollo Client handle this protocol for you, including the retry on PERSISTED_QUERY_NOT_FOUND.
The full schema is published as a downloadable file, so GraphQL tooling such as editor plugins and code generators can be pointed at a file instead of a live endpoint.
Download the Widgets API schema
Introspection queries are disabled in production. If your workflow requires introspecting the live endpoint, contact support to have it enabled for a specific environment.
The API is grouped by the resource it operates on:
- Users – read and update the signed-in user’s profile and email address.
- Organizations – manage members, invitations, and role assignments.
- Domain verification – add organization domains and track their DNS verification.
- Roles and permissions – define roles and inspect the permissions a member holds.
- Authentication – manage passwords, MFA factors, passkeys, and sessions.
- Single Sign-On – manage an organization’s SSO connections.
- Directory Sync – read directory connections and their synced users and groups.
- Audit logs – search and export audit events.
- Data providers – browse data providers and the user’s connections to them.