Authentication and Permissions
Platform OAuth Integration
Instant supports the standard OAuth 2.0 Authorization Code grant flow, enabling users to authorize your application to access their Instant data and perform actions on their behalf, like reading app details or managing apps.
This guide walks you through the steps required to integrate your application with Instant using OAuth. You can also walk-through a demo to see it in action.
OAuth flow
1. Create an OAuth App and Client
The first step is to register your OAuth application with Instant. This is done through the Instant Dashboard:
- Navigate to the OAuth Apps section of the Instant Dashboard
- Create a new "OAuth App" associated with your Instant App. Give it a descriptive name.
- Within that OAuth App, create a new "OAuth Client".
- Provide a name for the client (e.g., "My Web App Integration").
- Specify one or more Authorized Redirect URIs. These are the exact URLs that Instant is allowed to redirect the user back to after they authorize (or deny) your application.
- Upon creating the client, you will be provided with:
- Client ID: A public identifier for your application.
- Client Secret: A confidential secret known only to your application and Instant. Treat this like a password and keep it secure.
You will need the Client ID and Client Secret for subsequent steps.
Your OAuth app will start in test mode. Only members of your Instant app will be able to authorize with the app. Once you have your OAuth flow working, ping us in Discord to go live.
2. Redirect User for Authorization
To start the flow, redirect the user from your application to the Instant authorization endpoint. Construct the URL as follows:
Base URL:
https://api.instantdb.com/platform/oauth/start
Query Parameters:
client_id
(Required): Your OAuth Client ID obtained in Step 1.response_type
(Required): Must be set tocode
.redirect_uri
(Required): One of the exact Authorized Redirect URIs you registered for your client in Step 1.scope
(Required): A space-separated list of permissions your application is requesting. Available scopes are:apps-read
: Allows listing user's apps and viewing their schema/permissions.apps-write
: Allows creating/deleting apps and updating schema/permissions.
state
(Required): A random, opaque string generated by your application. This value is used to prevent Cross-Site Request Forgery (CSRF) attacks. You should generate a unique value for each authorization request and store it (e.g., in the user's session) to verify later.
Example Authorization URL:
https://api.instantdb.com/platform/oauth/start?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REDIRECT_URI&scope=apps-read%20apps-write&state=RANDOM_STATE_STRING
When the user visits this URL, they will be prompted by Instant to log in (if they aren't already) and asked to grant your application the requested permissions (scopes).
3. Handle the Redirect from Instant
After the user grants or denies authorization, Instant redirects them back to the redirect_uri
you specified.
If Successful:
The redirect URL will include code
and state
query parameters:
YOUR_REDIRECT_URI?code=AUTHORIZATION_CODE&state=RANDOM_STATE_STRING
- Verify the
state
parameter: Check that the receivedstate
value matches the one you generated in Step 2 for this user. If they don't match, reject the request to prevent CSRF attacks. - Extract the
code
: This is a short-lived, single-use authorization code.
If Unsuccessful:
The redirect URL will include error
and potentially error_description
parameters:
YOUR_REDIRECT_URI?error=access_denied&state=RANDOM_STATE_STRING
Handle these errors appropriately (e.g., display a message to the user).
4. Exchange Authorization Code for Tokens
Once you have verified the state
and obtained the code
, exchange the code for an access token and a refresh token by making a POST
request from your backend server to the Instant token endpoint.
Endpoint:
https://api.instantdb.com/platform/oauth/token
Method: POST
Headers:
Content-Type: application/json
Request Body (JSON):
{ "grant_type": "authorization_code", "code": "YOUR_AUTHORIZATION_CODE", // The code from Step 3 "redirect_uri": "YOUR_REDIRECT_URI", // The same redirect URI used in Step 2 "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET" }
Example curl
:
export CLIENT_ID="YOUR_CLIENT_ID" export CLIENT_SECRET="YOUR_CLIENT_SECRET" export REDIRECT_URI="YOUR_REDIRECT_URI" export CODE="YOUR_AUTHORIZATION_CODE" curl -v -X POST "https://api.instantdb.com/platform/oauth/token" \ -H "Content-Type: application/json" \ -d "{ \"client_id\": \"$CLIENT_ID\", \"client_secret\": \"$CLIENT_SECRET\", \"redirect_uri\": \"$REDIRECT_URI\", \"grant_type\": \"authorization_code\", \"code\": \"$CODE\" }"
Successful Response (JSON):
{ "access_token": "ACCESS_TOKEN_VALUE", "refresh_token": "REFRESH_TOKEN_VALUE", "expires_in": 1209600, // Lifetime in seconds (e.g., 2 weeks) "scope": "apps-read apps-write", // Scopes granted "token_type": "Bearer" }
access_token
: The token used to authenticate API requests on behalf of the user. It has a limited lifetime (expires_in
).refresh_token
: A long-lived token used to obtain new access tokens when the current one expires. Store this securely, associated with the user.expires_in
: The number of seconds until theaccess_token
expires.scope
: The actual scopes granted by the user (may be different from requested).
Store the access_token
, refresh_token
, and expiration time securely on your backend, associated with the user who authorized your application.
5. Use the Access Token
To make authenticated API calls to Instant on behalf of the user, include the access_token
in the Authorization
header of your requests:
Authorization: Bearer ACCESS_TOKEN_VALUE
Example curl
(Fetching User's Apps):
export ACCESS_TOKEN="ACCESS_TOKEN_VALUE" curl -v "https://api.instantdb.com/superadmin/apps" \ -H "Authorization: Bearer $ACCESS_TOKEN"
This allows you to perform actions permitted by the granted scopes.
6. Refresh the Access Token
Access tokens expire. When an access token expires, or shortly before it does, use the refresh_token
obtained in Step 4 to get a new access_token
without requiring the user to go through the authorization flow again.
Make a POST
request from your backend server to the token endpoint:
Endpoint:
https://api.instantdb.com/platform/oauth/token
Method: POST
Headers:
Content-Type: application/json
Request Body (JSON):
{ "grant_type": "refresh_token", "refresh_token": "REFRESH_TOKEN_VALUE", // The refresh token stored earlier "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET" }
Example curl
:
export CLIENT_ID="YOUR_CLIENT_ID" export CLIENT_SECRET="YOUR_CLIENT_SECRET" export REFRESH_TOKEN="REFRESH_TOKEN_VALUE" curl -v -X POST "[https://api.instantdb.com/platform/oauth/token](https://api.instantdb.com/platform/oauth/token)" \ -H "Content-Type: application/json" \ -d "{ \"client_id\": \"$CLIENT_ID\", \"client_secret\": \"$CLIENT_SECRET\", \"grant_type\": \"refresh_token\", \"refresh_token\": \"$REFRESH_TOKEN\" }"
Successful Response (JSON):
The response format is similar to the code exchange, providing a new access token and potentially a new refresh token:
{ "access_token": "NEW_ACCESS_TOKEN_VALUE", "refresh_token": "NEW_REFRESH_TOKEN_VALUE", // May or may not be included/changed "expires_in": 1209600, "scope": "apps-read apps-write", "token_type": "Bearer" }
- Update the stored access token and expiration time for the user.
- If the refresh token request fails (e.g., the refresh token was revoked), you will need to direct the user through the authorization flow (Step 2) again.
7. Invalidate a token
You can invalidate an access token or a refresh token through the revoke
endpoint.
Endpoint:
https://api.instantdb.com/platform/oauth/revoke
Method: POST
Query Parameters:
token
(Required): The token you want to invalidate
Example URL:
https://api.instantdb.com/platform/oauth/revoke?token=YOUR_TOKEN
Endpoints
List Apps
- Description: Retrieves a list of all applications created by the authenticated user.
- Method:
GET
- Path:
/superadmin/apps
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-read
- Success Response:
- Code:
200 OK
- Body:
{ "apps": [ { "id": "uuid", "title": "string", "creator_id": "uuid", "created_at": "timestamp" } // ... more apps ] }
- Code:
Get App Details
- Description: Retrieves details for a specific application.
- Method:
GET
- Path:
/superadmin/apps/:app_id
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-read
- Path Parameters:
app_id
(UUID, required): The ID of the application to retrieve. The authenticated user must be the creator.
- Success Response:
- Code:
200 OK
- Body:
{ "app": { "id": "uuid", "title": "string", "creator_id": "uuid", "created_at": "timestamp" } }
- Code:
- Error Responses:
404 Not Found
: If the app doesn't exist or doesn't belong to the user.
Create App
- Description: Creates a new application.
- Method:
POST
- Path:
/superadmin/apps
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-write
- Request Body:
{ "title": "New App Name" }
title
(string, required): The desired name for the new application. Must not be blank.
- Success Response:
- Code:
200 OK
- Body:
{ "app": { "id": "uuid", "title": "string", "creator_id": "uuid", "created_at": "timestamp" } }
- Code:
Update App (Rename)
- Description: Updates the details of a specific application (currently only supports renaming).
- Method:
POST
- Path:
/superadmin/apps/:app_id
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-write
- Path Parameters:
app_id
(UUID, required): The ID of the application to update. The authenticated user must be the creator.
- Request Body:
{ "title": "New App Name" }
title
(string, required): The new desired name for the application. Must not be blank.
- Success Response:
- Code:
200 OK
- Body:
{ "app": { "id": "uuid", "title": "string", "creator_id": "uuid", "created_at": "timestamp" } }
- Code:
- Error Responses:
404 Not Found
: If the app doesn't exist or doesn't belong to the user.400 Bad Request
: If the title is blank or invalid.
Delete App
- Description: Marks an application for deletion. The app data may be retained for a period before permanent removal.
- Method:
DELETE
- Path:
/superadmin/apps/:app_id
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-write
- Path Parameters:
app_id
(UUID, required): The ID of the application to delete. The authenticated user must be the creator.
- Success Response:
- Code:
200 OK
- Body:
{ "app": { "id": "uuid", "title": "string", "creator_id": "uuid", "created_at": "timestamp" } }
- Code:
- Error Responses:
404 Not Found
: If the app doesn't exist or doesn't belong to the user.
Get Permissions (Rules)
- Description: Retrieves the current permission rules for the application.
- Method:
GET
- Path:
/superadmin/apps/:app_id/perms
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-read
- Path Parameters:
app_id
(UUID, required): The ID of the application.
- Success Response:
- Code:
200 OK
- Body:
{ "perms": { // Permissions code object // e.g., {"posts": {"allow": {"read": "true", "create": "auth.id != null"}}} } }
- Code:
Set Permissions (Rules)
Description: Overwrites the existing permission rules for the application with the provided definition.
Method:
POST
Path:
/superadmin/apps/:app_id/perms
Authentication: Required (Bearer Token)
Required OAuth Scope:
apps-write
Path Parameters:
app_id
(UUID, required): The ID of the application.
Request Body:
{ "code": { // Permissions code object // e.g., {"posts": {"allow": {"read": "true", "create": "false"}}} } }
code
(object, required): The complete permission rules definition.
Success Response:
Code:
200 OK
Body:
{ "rules": { // Permissions code object // e.g., {"posts": {"allow": {"read": "true", "create": "false"}}} } }
Error Responses:
400 Bad Request
: If the providedcode
object fails validation.
Get schema
- Description: Views the schema for the app.
- Method:
POST
- Path:
/superadmin/apps/:app_id/schema
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-read
- Path Parameters:
app_id
(UUID, required): The ID of the application.
- Success Response:
- Code:
200 OK
- Body: An object detailing the planned schema changes.
{ "schema": { "blobs": { "namespace-name": { "attribute-name": { "id": "uuid", "cardinality": "one | many", "forward-identity": ["uuid", "namespace-name", "attribute-name"], "index?": "boolean", "unique?": "boolean", "checked-data-type": "'string' | 'number' | 'boolean' | 'date' | null" } } // Rest of blob attrs }, "refs": { "ref-string": { "id": "uuid", "cardinality": "one | many", "forward-identity": ["uuid", "namespace-name", "attribute-name"], "reverse-identity": [ "uuid", "linked-namespace-name", "linked-attribute-name" ], "index?": "boolean", "unique?": "boolean" } // Rest of ref attrs } } }
- Code:
Plan Schema Push
- Description: Calculates the changes required to apply a new schema definition without actually applying them. Useful for previewing migrations.
- Method:
POST
- Path:
/superadmin/apps/:app_id/schema/push/plan
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-read
- Path Parameters:
app_id
(UUID, required): The ID of the application.
- Request Body:
{ "schema": { "entities": { "namespace-name": { "attrs": { "attribute-name": { "valueType": "'string' | 'number' | 'boolean' | 'date' | 'json'", "config": { "indexed": "boolean", "unique": "boolean" } } } } // Rest of entities }, "links": { "unique-string": { "forward": { "on": "forward-namespace-name", "label": "forward-attr-label", "has": "many | one", "onDelete": "cascade | null" }, "reverse": { "on": "reverse-namespace-name", "label": "reverse-attr-label", "has": "many | one" "onDelete": "cascade | null" } } // Rest of link attrs } } }
schema
(object, required): An object withentities
andlinks
that matches the structure of instant.schema.ts
- Success Response:
- Code:
200 OK
- Body:
{ "current-schema": "schema object (same structure as GET schema)", "new-schema": "schema object (same structure as GET schema)", "steps": [ [ "add-attr", { "id": "uuid", "cardinality": "one | many", "forward-identity": ["uuid", "namespace-name", "attribute-name"], "index?": "boolean", "unique?": "boolean", "checked-data-type": "'string' | 'number' | 'boolean' | 'date' | null" } ], [ "add-attr", { "id": "uuid", "cardinality": "one | many", "forward-identity": ["uuid", "namespace-name", "attribute-name"], "index?": "boolean", "unique?": "boolean", "checked-data-type": "'string' | 'number' | 'boolean' | 'date' | null" } ], [ "index", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "remove-index", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "unique", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "remove-unique", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "check-data-type", { "attr-id": "uuid",, "checked-data-type": "'string' | 'boolean' | 'number' | 'date'", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "remove-data-type", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ] ] }
- Code:
Apply Schema Push
- Description: Calculates and applies the schema changes based on the provided definition.
- Method:
POST
- Path:
/superadmin/apps/:app_id/schema/push/apply
- Authentication: Required (Bearer Token)
- Required OAuth Scope:
apps-write
- Path Parameters:
app_id
(UUID, required): The ID of the application.
- Request Body: Same as "Plan Schema Push".
{ "schema": { "entities": { "namespace-name": { "attrs": { "attribute-name": { "valueType": "'string' | 'number' | 'boolean' | 'date' | 'json'", "config": { "indexed": "boolean", "unique": "boolean" } } } } // Rest of entities }, "links": { "unique-string": { "forward": { "on": "forward-namespace-name", "label": "forward-attr-label", "has": "many | one", "onDelete": "cascade | null" }, "reverse": { "on": "reverse-namespace-name", "label": "reverse-attr-label", "has": "many | one" "onDelete": "cascade | null" } } // Rest of link attrs } } }
schema
(object, required): An object withentities
andlinks
that matches the structure of instant.schema.ts
- Success Response:
- Code:
200 OK
- Body:
{ "current-schema": "schema object (same structure as GET schema)", "new-schema": "schema object (same structure as GET schema)", "steps": [ [ "add-attr", { "id": "uuid", "cardinality": "one | many", "forward-identity": ["uuid", "namespace-name", "attribute-name"], "index?": "boolean", "unique?": "boolean", "checked-data-type": "'string' | 'number' | 'boolean' | 'date' | null" } ], [ "add-attr", { "id": "uuid", "cardinality": "one | many", "forward-identity": ["uuid", "namespace-name", "attribute-name"], "index?": "boolean", "unique?": "boolean", "checked-data-type": "'string' | 'number' | 'boolean' | 'date' | null" } ], [ "index", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "remove-index", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "unique", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "remove-unique", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "check-data-type", { "attr-id": "uuid",, "checked-data-type": "'string' | 'boolean' | 'number' | 'date'", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ], [ "remove-data-type", { "attr-id": "uuid", "forward-identity": ["uuid", "namespace-name", "attribute-name"] } ] ] }
- Code: