Authentication
Authenticating Calls
To authenticate API calls, the client must add the following header:
Authorization: Bearer TOKENwhere TOKEN is obtained by one of the methods below.
Token Types
There are three types of tokens which can have one of two access scopes.
| Type | Scope | |
|---|---|---|
Personal Access Token (PAT) |
personal |
Generated manually from the integrations page. Doesn't expire, and can be revoked at any time. |
Workspace Access Token (WAT) |
workspace |
Generated manually from the integrations page. Doesn't expire, and can be revoked at any time. |
| OAuth Token | personal,workspace |
Issued through the OAuth2 Flow. Expires, must be refreshed. |
| Scope | |
|---|---|
personal |
Provides the same access level as the user who authorized the token. |
workspace |
Provides FULL DATA ACCESS of the workspace. Can be generated by workspace admins only. |
NOTE: The
workspacescope is not available on all plans.
Check https://grain.com/pricing to see if your plan includes Workspace API.
Tokens with the personal scope, including Personal Access Tokens, also authenticate the Grain MCP server at https://api.grain.com/_/mcp.
Tokens with the workspace scope are not accepted by the MCP server.
OAuth2 Flow
This authentication method is for developers building integrations with Grain to be used by any Grain users.
The API supports a standard OAuth2 Authorization Code flow, including the PKCE extension for client-side only authentication. At least one redirect URI is required to register a new client. For browser-based client applications, a list of CORS origins can also be added.
Authorization Code
Step 1
Obtain the client_id and client_secret (only for server-side applications) credentials.
Step 2
Open the url https://grain.com/_/public-api/oauth2/authorize with the following query params.
| Param | |
|---|---|
| client_id | Obtained separately in Step 1 |
| redirect_uri | Must exactly match a registered redirect URI. On localhost, 127.0.0.1 and [::1], any port matches |
| response_type | Must be code |
| state | Optional. Grain returns it unchanged in Step 4. PKCE already protects the flow, so state is only needed if your client relies on it |
| code_challenge | A random string, hashed with SHA256 and base64 URL encoded |
| code_challenge_method | Must be S256 |
| scopes | personal or workspace. Defaults to personal |
Step 3
If the user is not already signed in to Grain, they will be prompted to sign in. After being authenticated, the user will be prompted to confirm the OAuth2 connection.
Step 4
The user will be redirected to redirect_uri from Step 2 with the code query param, and state if you sent one.
Step 5
Make a POST request to generate the token.
See OAuth2 Generate Token
Diagram
sequenceDiagram participant you as You actor user as User participant grain as Grain Note right of you: Step 1 you-->>grain: Request Client ID & Secret (manually) grain-->>you: Client ID & Secret Note right of you: Step 2 user->>you: Requests OAuth you->>user: Open or Redirects to oauth2/authorize user->>grain: oauth2/authorize Note left of grain: Step 3 grain->>user: Requests login user->>grain: Login grain->>user: Requests Confirm OAuth user->>grain: Confirms Note left of grain: Step 4 grain->>user: Redirects to redirect_uri w/code user->>you: redirect_uri w/code Note right of you: Step 5 you->>grain: POST oauth2/token w/code grain->>you: access_token & refresh_token Note right of you: Refresh you->>grain: POST oauth2/token w/refresh_token grain->>you: access_token & refresh_token
Client ID Metadata Document
Public clients, such as MCP clients and native apps, can skip Step 1 by using an HTTPS URL as their client_id.
That URL must serve a JSON Client ID Metadata Document describing the client.
{
"client_id": "https://app.example.com/oauth/client.json",
"client_name": "Example App",
"redirect_uris": ["https://app.example.com/oauth/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}| Field | |
|---|---|
| client_id | Required. Must exactly match the URL the document is served from |
| redirect_uris | Required. The redirect_uri in Step 2 must exactly match one of these |
| client_name | Shown to the user when they confirm the connection. Defaults to the URL's host |
| grant_types | Must include authorization_code. Add refresh_token to receive refresh tokens |
| response_types | Must be code if present |
| token_endpoint_auth_method | Must be none if present. Client secrets are not supported |
Grain applies these rules to documents and their clients:
- The URL must use
httpsand serve the document directly, with no redirects, in at most 5 KB. - Grain caches the document for the
max-agein itsCache-Controlheader, between 5 minutes and 24 hours, and revalidates it with itsETag. - PKCE is required, so send
code_challengein Step 2 andcode_verifierwhen generating the token. httpredirect URIs are only allowed onlocalhost,127.0.0.1and[::1], where any port matches.- The
workspacescope is only available to clients verified by Grain.
Refresh Token
After the access_token has expired, you may generate a new one.
