Auth Hub API
All endpoints are prefixed with /api/v1/:projectId. Auth endpoints use JWT or one-time tokens. Database endpoints require a project API key. Social login (Google, GitHub) uses OAuth 2.0 redirect flows. Webhooks notify your servers of events. Realtime provides WebSocket subscriptions for database changes.
Authentication
Bearer token flow — use /login to get an access_token (15 min) + refresh_token (30 days). Pass the access token as Authorization: Bearer <jwt>. Rotate via /refresh before expiry.
Cookie session flow — use POST /session to authenticate. Tokens are stored as HttpOnly cookies and auto-refreshed on GET /session. Ideal for SSR apps.
Auth endpoints (/session, /login, /register, /recover) require Authorization: Bearer <auth_api_key> — the Auth API Key from project settings.
Database endpoints use Authorization: Bearer <api_key> — the Database API Key from project settings. Both keys start with sk_.
Authentication
8 endpoints/api/v1/:projectId/registerBearer API KeyRegister
Creates a new user account in the project. If the project has SMTP configured, a verification email is sent automatically.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| string | Yes | Valid email address for the new account. | |
| password | string | Yes | Plain-text password (min 8 chars). Stored as bcrypt hash. |
| redirect_to | string | No | Relative path to redirect the user after email confirmation (e.g. /login?confirmed=true). Must start with /. If omitted, redirects to the project root. |
Notes
- ›Requires the project's Auth API Key in Authorization: Bearer <auth_api_key> — generated from the project settings page.
- ›redirect_to is embedded in the verification JWT. When the user clicks the confirmation link, Auth Hub redirects to {project.url}{redirect_to}.
- ›Only relative paths are accepted (must start with /). Absolute URLs are rejected to prevent open redirect attacks.
curl -X POST https://your-domain.com/api/v1/:projectId/register \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <auth_api_key>" \
-d '{
"email": "[email protected]",
"password": "securepassword123",
"redirect_to": "/login?confirmed=true"
}'{
"message": "Registration successful. Check your email to confirm.",
"user": {
"id": "bc0ede1a-...",
"email": "[email protected]"
}
}/api/v1/:projectId/verifyQuery TokenVerify Email
Confirms a user's email using the one-time token sent during registration. On success, redirects to {project.url}{redirect_to} if a redirect path was provided at registration, or to {project.url}?confirmed=true otherwise.
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| token | string | Yes | One-time verification token from the confirmation email. |
Notes
- ›This endpoint is typically called by the email client, not directly by your app.
- ›If the project has no URL configured, returns a JSON response instead of redirecting.
curl -X GET "https://your-domain.com/api/v1/:projectId/verify?token=eyJhbGci..."
{
"message": "Email confirmed successfully"
}/api/v1/:projectId/loginBearer API KeyLogin
Validates credentials and returns a signed JWT access token (HS256, 15-minute expiry) and a refresh token (30-day expiry). The email must be verified before login is allowed.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| string | Yes | Registered email address. | |
| password | string | Yes | Account password. |
Notes
- ›Requires the project's Auth API Key in Authorization: Bearer <auth_api_key> — generated from the project settings page.
- ›access_token expires in 15 minutes (900 seconds). Use /refresh to obtain a new pair.
- ›refresh_token is single-use and rotates on every refresh. Store it securely (HttpOnly cookie recommended).
- ›For cookie-based sessions, use POST /session instead — it handles token storage automatically.
curl -X POST https://your-domain.com/api/v1/:projectId/login \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <auth_api_key>" \
-d '{
"email": "[email protected]",
"password": "securepassword123"
}'{
"access_token": "eyJhbGci...",
"refresh_token": "a3f8c2d1e4b7...",
"token_type": "bearer",
"expires_in": 900,
"user": {
"id": "bc0ede1a-...",
"email": "[email protected]",
"confirmed_at": "2026-03-09T10:00:00Z",
"app_metadata": {
"provider": "email"
},
"user_metadata": {}
}
}/api/v1/:projectId/userBearer JWTGet User
Returns the full profile of the currently authenticated user. Requires a valid JWT in the Authorization header.
Notes
- ›Tokens that have been explicitly revoked via /logout are rejected even if they have not yet expired.
curl -X GET https://your-domain.com/api/v1/:projectId/user \ -H "Authorization: Bearer <access_token>"
{
"id": "bc0ede1a-...",
"email": "[email protected]",
"confirmed_at": "2026-03-09T10:00:00Z",
"created_at": "2026-03-09T10:00:00Z",
"app_metadata": {
"provider": "email"
},
"user_metadata": {}
}/api/v1/:projectId/refreshPublicRefresh Token
Exchanges a valid refresh token for a new access token + rotated refresh token. The old refresh token is immediately invalidated (rotation strategy). If a revoked token is presented, all tokens for that user are revoked as a reuse-attack countermeasure.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| refresh_token | string | Yes | The refresh token obtained from /login or a previous /refresh call. |
Notes
- ›Each refresh token is single-use. Save the new refresh_token from the response immediately.
- ›Reuse of an already-consumed token triggers a security lockout: all active refresh tokens for that user in this project are revoked.
- ›Refresh tokens expire after 30 days of inactivity.
curl -X POST https://your-domain.com/api/v1/:projectId/refresh \
-H "Content-Type: application/json" \
-d '{ "refresh_token": "a3f8c2d1e4b7..." }'{
"access_token": "eyJhbGci...",
"refresh_token": "9f1d3a2c8e5b...",
"token_type": "bearer",
"expires_in": 900,
"user": {
"id": "bc0ede1a-...",
"email": "[email protected]",
"confirmed_at": "2026-03-09T10:00:00Z",
"app_metadata": {
"provider": "email"
},
"user_metadata": {}
}
}/api/v1/:projectId/logoutBearer JWTLogout
Revokes the current access token by adding its JTI to the blocklist, and invalidates all refresh tokens for the user. Works even if the access token is already expired.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| refresh_token | string | No | If provided, only this specific refresh token is revoked instead of all of them. |
Notes
- ›The Authorization header is required even for expired tokens — the token is decoded (not verified) to extract the JTI for blocklisting.
- ›For cookie-based sessions, use DELETE /session instead — it clears the cookies automatically.
curl -X POST https://your-domain.com/api/v1/:projectId/logout \ -H "Authorization: Bearer <access_token>"
{
"message": "Logged out successfully"
}/api/v1/:projectId/recoverBearer API KeyRequest Password Reset
Generates a one-time password-reset token (1 h expiry) and sends it to the email address via the project's SMTP relay. Always returns the same response regardless of whether the email exists — to prevent user enumeration.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| string | Yes | Email address of the account to recover. |
Notes
- ›Requires the project's Auth API Key in Authorization: Bearer <auth_api_key> — generated from the project settings page.
curl -X POST https://your-domain.com/api/v1/:projectId/recover \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <auth_api_key>" \
-d '{ "email": "[email protected]" }'{
"message": "If the email exists, a recovery link will be sent"
}/api/v1/:projectId/reset-passwordQuery TokenReset Password
Sets a new password using the recovery token. The token is single-use and is invalidated after a successful reset.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| token | string | Yes | One-time recovery token from the reset email. |
| newPassword | string | Yes | New plain-text password (min 8 chars). |
curl -X POST https://your-domain.com/api/v1/:projectId/reset-password \
-H "Content-Type: application/json" \
-d '{
"token": "<recovery_token>",
"newPassword": "newsecurepassword456"
}'{
"message": "Password updated successfully"
}Cookie Session
3 endpoints/api/v1/:projectId/sessionBearer API KeyCreate Session
Authenticates with email + password and sets HttpOnly cookie-based session tokens. The access token (ah_access_token) and refresh token (ah_refresh_token) are stored as HttpOnly cookies — they are never exposed in the response body. Designed for server-rendered apps.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| string | Yes | Registered email address. | |
| password | string | Yes | Account password. |
Notes
- ›Requires the project's Auth API Key in Authorization: Bearer <auth_api_key> — generated from the project settings page. This call is typically made from your server, never directly from the browser.
- ›Sets two HttpOnly cookies: ah_access_token (15 min) and ah_refresh_token (30 days). Tokens are NOT in the response body.
- ›Cookies are Secure, SameSite=Lax, and scoped to the project path.
curl -X POST https://your-domain.com/api/v1/:projectId/session \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <auth_api_key>" \
-c cookies.txt \
-d '{
"email": "[email protected]",
"password": "securepassword123"
}'{
"user": {
"id": "bc0ede1a-...",
"email": "[email protected]",
"confirmed_at": "2026-03-09T10:00:00Z",
"app_metadata": {
"provider": "email"
},
"user_metadata": {}
}
}/api/v1/:projectId/sessionCookie SessionGet Session
Reads the current session from cookies. If the access token is expired but the refresh token is valid, silently rotates both tokens and returns the user. Returns 401 if no valid session exists.
Notes
- ›Use this endpoint in your middleware or server components to check authentication status.
- ›When the access token is silently refreshed, updated Set-Cookie headers are included in the response.
curl -X GET https://your-domain.com/api/v1/:projectId/session \ -b cookies.txt
{
"user": {
"id": "bc0ede1a-...",
"email": "[email protected]",
"confirmed_at": "2026-03-09T10:00:00Z",
"app_metadata": {
"provider": "email"
},
"user_metadata": {}
}
}/api/v1/:projectId/sessionCookie SessionDelete Session
Destroys the cookie session: revokes the access token (adds JTI to blocklist), invalidates all refresh tokens for the user, and clears both cookies.
Notes
- ›Equivalent to /logout but designed for cookie-based sessions — no Authorization header needed.
curl -X DELETE https://your-domain.com/api/v1/:projectId/session \ -b cookies.txt
{
"message": "Logged out successfully"
}Database CRUD
4 endpoints/api/v1/:projectId/db/createBearer API KeyInsert Row
Inserts a single row into the specified table and returns the created record. The project's database must be enabled and a valid API key must be provided.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| table | string | Yes | Name of the target table. |
| data | object | Yes | Columns to insert as key-value pairs. Omit auto-generated columns (e.g. SERIAL / IDENTITY ids) — PostgreSQL fills them automatically and returns them in the response. |
Notes
- ›Do not include auto-incremented columns (SERIAL, BIGSERIAL, GENERATED ALWAYS AS IDENTITY) in data — omit them and PostgreSQL generates the value. The full row including the generated id is returned via RETURNING *.
- ›Column names are validated against [a-zA-Z0-9_.] — injection via column names is blocked at the application layer.
curl -X POST https://your-domain.com/api/v1/:projectId/db/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <api_key>" \
-d '{
"table": "users",
"data": { "name": "Alice", "email": "[email protected]" }
}'
# Note: "id" is not sent — it is auto-generated by PostgreSQL and returned in the response.{
"data": {
"rows": [
{
"id": 1,
"name": "Alice",
"email": "[email protected]"
}
]
}
}/api/v1/:projectId/db/readBearer API KeyQuery Rows
Queries rows from a table with field selection, advanced filtering with comparison operators, multi-column ordering, and pagination.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| table | string | Yes | Name of the table to query. |
| select | string[] | No | Array of column names to return. Defaults to all columns ("*"). Example: ["id", "name", "price"] |
| where | object | No | Filter conditions. Supports simple equality { "col": value } or operator syntax { "col": { "$gt": 10 } }. All conditions are ANDed. See operators below. |
| limit | number | No | Max rows to return. Defaults to 100, hard capped at 1000. |
| offset | number | No | Number of rows to skip (for pagination). Defaults to 0. |
| orderBy | object | object[] | No | Sort order. Single: { "column": "price", "order": "DESC" }. Multi: [{ "column": "price", "order": "DESC" }, { "column": "name", "order": "ASC" }] |
Notes
- ›Operators: $eq (=), $neq (!=), $gt (>), $gte (>=), $lt (<), $lte (<=) — comparison operators for numbers, strings, and dates.
- ›$in / $nin — match against a list of values. Example: { "status": { "$in": ["active", "pending"] } }
- ›$like / $ilike — SQL pattern matching with % and _ wildcards. $ilike is case-insensitive. Example: { "name": { "$ilike": "%widget%" } }
- ›$is / $not — NULL and boolean checks. { "$is": null } → IS NULL, { "$not": null } → IS NOT NULL. Also accepts true/false.
- ›Multiple operators on the same column create a range: { "price": { "$gte": 10, "$lte": 100 } } → price BETWEEN 10 AND 100.
- ›Simple equality still works: { "status": "active" } is equivalent to { "status": { "$eq": "active" } }.
curl -X POST https://your-domain.com/api/v1/:projectId/db/read \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <api_key>" \
-d '{
"table": "products",
"select": ["id", "name", "price"],
"where": {
"price": { "$gte": 10, "$lte": 100 },
"status": { "$in": ["active", "sale"] },
"deleted_at": { "$is": null }
},
"orderBy": [
{ "column": "price", "order": "ASC" },
{ "column": "name", "order": "DESC" }
],
"limit": 50,
"offset": 0
}'{
"data": {
"rows": [
{
"id": 3,
"name": "Widget Pro",
"price": 29.99
},
{
"id": 7,
"name": "Widget Basic",
"price": 12.5
}
],
"count": 2
}
}/api/v1/:projectId/db/updateBearer API KeyUpdate Rows
Updates rows matching the where conditions and returns the affected records. The where field is required and must be non-empty — omitting it would update every row in the table.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| table | string | Yes | Name of the table to update. |
| data | object | Yes | Fields to update as key-value pairs. Must be non-empty. |
| where | object | Yes | Filter conditions. Supports the same operator syntax as /db/read ($gt, $in, $like, etc.). Must be non-empty — empty where is rejected with 400. |
Notes
- ›An empty where object {} is rejected with 400 to prevent accidental full-table updates.
- ›Updated rows are returned in full via RETURNING *.
curl -X POST https://your-domain.com/api/v1/:projectId/db/update \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <api_key>" \
-d '{
"table": "users",
"data": { "active": false },
"where": { "id": 1 }
}'{
"data": {
"updated": 1,
"rows": [
{
"id": 1,
"name": "Alice",
"active": false
}
]
}
}/api/v1/:projectId/db/deleteBearer API KeyDelete Rows
Deletes rows matching the where conditions and returns the deleted records. The where field is required and must be non-empty — omitting it would delete every row in the table.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| table | string | Yes | Name of the table to delete from. |
| where | object | Yes | Filter conditions. Supports the same operator syntax as /db/read ($gt, $in, $like, etc.). Must be non-empty — empty where is rejected with 400. |
Notes
- ›An empty where object {} is rejected with 400 to prevent accidental full-table deletes.
- ›Deleted rows are returned in full via RETURNING *.
curl -X POST https://your-domain.com/api/v1/:projectId/db/delete \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <api_key>" \
-d '{
"table": "users",
"where": { "id": 1 }
}'{
"data": {
"deleted": 1,
"rows": [
{
"id": 1,
"name": "Alice",
"active": false
}
]
}
}Storage
7 endpoints/api/v1/:projectId/storage/bucketsBearer API KeyCreate Bucket
Creates a new storage bucket for the project. Buckets organize files and control access (public vs private) and upload limits.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Bucket name. 2-63 chars, lowercase alphanumeric, dashes, underscores. Must start with a letter or number. |
| isPublic | boolean | No | If true, files can be downloaded without authentication. Default: false. |
| maxFileSize | number | No | Max file size in bytes for this bucket. Default: 0 (no limit). Set a positive value to enforce a per-file size cap. |
| allowedMimeTypes | string | No | Comma-separated MIME types. Supports wildcards: "image/*,application/pdf". Default: all types allowed. |
curl -X POST https://your-domain.com/api/v1/:projectId/storage/buckets \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <api_key>" \
-d '{
"name": "avatars",
"isPublic": true,
"maxFileSize": 5242880
}'{
"data": {
"id": "uuid",
"name": "avatars",
"isPublic": true,
"maxFileSize": 5242880,
"allowedMimeTypes": null,
"createdAt": "2024-01-01T00:00:00.000Z"
}
}/api/v1/:projectId/storage/bucketsBearer API KeyList Buckets
Lists all storage buckets for the project, including object count per bucket.
curl https://your-domain.com/api/v1/:projectId/storage/buckets \ -H "Authorization: Bearer <api_key>"
{
"data": [
{
"id": "uuid",
"name": "avatars",
"isPublic": true,
"maxFileSize": 5242880,
"_count": {
"objects": 12
}
},
{
"id": "uuid",
"name": "documents",
"isPublic": false,
"maxFileSize": 10485760,
"_count": {
"objects": 3
}
}
]
}/api/v1/:projectId/storage/bucketsBearer API KeyDelete Bucket
Deletes a bucket and ALL its files permanently. This cannot be undone.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Name of the bucket to delete. |
curl -X DELETE https://your-domain.com/api/v1/:projectId/storage/buckets \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <api_key>" \
-d '{ "name": "avatars" }'{
"data": {
"deleted": true
}
}/api/v1/:projectId/storage/uploadBearer API KeyUpload File
Uploads a file to a bucket. Uses multipart/form-data. If a file with the same key already exists, it is overwritten.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| file | File | Yes | The file to upload (multipart form field). |
| bucket | string | Yes | Target bucket name. |
| key | string | Yes | Object key (path within bucket). Example: "avatars/user1.png" or "docs/report.pdf". |
Notes
- ›Content-Type must be multipart/form-data (not application/json).
- ›File size is validated against the bucket's maxFileSize and the project's storage quota.
- ›If the bucket has allowedMimeTypes set, the file's MIME type is validated. Wildcards like image/* are supported.
- ›Uploading to an existing key overwrites the file.
curl -X POST https://your-domain.com/api/v1/:projectId/storage/upload \ -H "Authorization: Bearer <api_key>" \ -F "[email protected]" \ -F "bucket=avatars" \ -F "key=users/user1.jpg"
{
"data": {
"id": "uuid",
"key": "users/user1.jpg",
"size": 45231,
"mimeType": "image/jpeg",
"bucketId": "uuid",
"createdAt": "2024-01-01T00:00:00.000Z"
}
}/api/v1/:projectId/storage/object/:bucket/:keyBearer API KeyDownload File
Downloads a file by bucket name and key. Public bucket files are accessible without authentication. Private bucket files require an API key.
Notes
- ›The URL path is: /storage/object/{bucketName}/{objectKey}. Nested keys use path segments: /storage/object/avatars/users/user1.jpg
- ›Public buckets: No Authorization header needed. Private buckets: Bearer API Key required.
- ›Returns the raw file with appropriate Content-Type header.
- ›Responses are cached for 1 hour (Cache-Control: public, max-age=3600).
# Public bucket (no auth needed) curl https://your-domain.com/api/v1/:projectId/storage/object/avatars/users/user1.jpg # Private bucket (auth required) curl https://your-domain.com/api/v1/:projectId/storage/object/documents/report.pdf \ -H "Authorization: Bearer <api_key>"
{
"(raw file bytes with Content-Type header)": ""
}/api/v1/:projectId/storage/object/:bucket/:keyBearer API KeyDelete File
Deletes a file from a bucket by its key.
Notes
- ›The URL path is: /storage/object/{bucketName}/{objectKey}.
curl -X DELETE https://your-domain.com/api/v1/:projectId/storage/object/avatars/users/user1.jpg \ -H "Authorization: Bearer <api_key>"
{
"data": {
"deleted": true
}
}/api/v1/:projectId/storage/listBearer API KeyList Objects
Lists objects in a bucket with optional prefix filtering and pagination.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| bucket | string | Yes | Bucket name to list objects from. |
| prefix | string | No | Filter objects by key prefix. Example: "users/" to list all objects under the users directory. |
| limit | number | No | Max objects to return. Default: 100, max: 1000. |
| offset | number | No | Number of objects to skip (for pagination). Default: 0. |
curl -X POST https://your-domain.com/api/v1/:projectId/storage/list \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <api_key>" \
-d '{
"bucket": "avatars",
"prefix": "users/",
"limit": 50
}'{
"data": {
"objects": [
{
"id": "uuid",
"key": "users/user1.jpg",
"size": 45231,
"mimeType": "image/jpeg",
"createdAt": "2024-01-01T00:00:00.000Z"
},
{
"id": "uuid",
"key": "users/user2.png",
"size": 12400,
"mimeType": "image/png",
"createdAt": "2024-01-02T00:00:00.000Z"
}
],
"count": 2
}
}Social Login (OAuth)
2 endpoints/api/v1/:projectId/auth/:providerPublicStart OAuth Flow
Initiates an OAuth 2.0 authorization flow by redirecting the user to the provider's consent screen. Supported providers: google, github. The provider must be configured in the project's Auth Providers settings.
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| redirect_to | string | No | Relative path to redirect the user after authentication (e.g. /dashboard). Must start with /. |
Notes
- ›No API key is required — CSRF protection is handled via a signed state JWT.
- ›The provider (google or github) must be configured with Client ID and Client Secret in the project dashboard under Auth Providers.
- ›The redirect_to parameter is embedded in the state JWT and used after the callback to redirect the user back to your app.
# Open in browser — this is a redirect, not an API call https://your-domain.com/api/v1/:projectId/auth/google https://your-domain.com/api/v1/:projectId/auth/github # With redirect path https://your-domain.com/api/v1/:projectId/auth/google?redirect_to=/dashboard
{
"(302 Redirect to provider consent screen)": ""
}/api/v1/:projectId/auth/callbackPublicOAuth Callback
Handles the OAuth provider callback after user authorization. Exchanges the authorization code for provider tokens, fetches user info, creates or links the user account, issues access + refresh tokens, and redirects to the client app with tokens in the URL hash fragment.
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| code | string | Yes | Authorization code from the OAuth provider. |
| state | string | Yes | Signed state JWT for CSRF verification. |
Notes
- ›Tokens are passed via URL hash fragment (#), not query parameters — hash fragments are never sent to the server, following the same pattern as Supabase.
- ›If the email already exists for this project, the account is linked to the OAuth provider and the user is logged in.
- ›New users created via OAuth are automatically confirmed (no email verification needed).
- ›Social-only users have no password. They cannot use the /login endpoint — they must authenticate via OAuth.
- ›Rate limited: 20 requests per IP per 15 minutes.
# This endpoint is called automatically by the OAuth provider.
# After successful auth, the user is redirected to:
# {project.url}{redirect_to}#access_token=...&refresh_token=...&expires_in=900&provider=google
# Extract tokens from the hash fragment in your frontend:
const hash = new URLSearchParams(window.location.hash.slice(1));
const accessToken = hash.get("access_token");
const refreshToken = hash.get("refresh_token");{
"(302 Redirect to client app with tokens in hash fragment)": "",
"hash_params": {
"access_token": "eyJhbGci...",
"refresh_token": "a3f8c2d1e4b7...",
"token_type": "bearer",
"expires_in": "900",
"provider": "google"
}
}Webhooks
1 endpoints(configured webhook URL)PublicWebhook Delivery
Auth Hub sends webhook events to your configured endpoints when key events occur. Webhooks are configured via the project dashboard. Each delivery includes an HMAC-SHA256 signature for verification.
Body Parameters
| Param | Type | Required | Description |
|---|---|---|---|
| event | string | Yes | Event name: user.registered, user.login, user.confirmed, user.oauth_login, password.reset, test.ping |
| data | object | Yes | Event payload containing relevant data (e.g. user id and email). |
| timestamp | string | Yes | ISO 8601 timestamp of when the event occurred. |
Notes
- ›Webhooks are fire-and-forget: they do not block API responses.
- ›Failed deliveries are retried with exponential backoff: 30s, 120s, 480s (max 3 attempts).
- ›Each delivery includes an X-AuthHub-Signature header with an HMAC-SHA256 signature. Always verify this signature to ensure the payload is authentic.
- ›The signing secret is generated when you create the webhook and shown once. Store it securely.
- ›Delivery timeout is 10 seconds. Your endpoint must respond within this time.
- ›Available events: user.registered, user.login, user.confirmed, user.oauth_login, password.reset, test.ping.
# Example webhook payload sent to your endpoint:
# POST https://your-app.com/webhooks/authhub
# Headers:
# Content-Type: application/json
# X-AuthHub-Signature: sha256=<hmac_hex>
# X-AuthHub-Event: user.registered
# X-AuthHub-Delivery: <delivery_id>
# Verify the signature in your handler:
import crypto from "crypto";
function verifyWebhook(payload, signature, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}{
"event": "user.registered",
"data": {
"user": {
"id": "bc0ede1a-...",
"email": "[email protected]"
}
},
"timestamp": "2026-03-13T10:00:00.000Z"
}Realtime (WebSocket)
1 endpointsws://host:4000/realtime/v1/:projectIdBearer JWTWebSocket Connection
Connect to the realtime WebSocket server to receive live database change notifications. The server runs as a separate process on port 4000 (configurable via REALTIME_PORT). Requires a valid JWT access token for authentication.
Notes
- ›The realtime server runs as a sidecar process, separate from the Next.js app. Start it with: npx tsx src/realtime/server.ts
- ›Authentication must be the first message sent within 10 seconds of connecting, or the connection is closed.
- ›Channel format is table:<tableName>. The table must have a realtime trigger installed via the dashboard.
- ›Realtime must be enabled for the project, and the project must have a database enabled.
- ›Max 100 connections per project. Server sends ping every 30s; clients that miss pong are disconnected.
- ›Realtime triggers use PostgreSQL LISTEN/NOTIFY. The payload includes operation type and row ID (not full row data, due to pg_notify 8KB limit).
# Connect using wscat or any WebSocket client:
wscat -c ws://localhost:4000/realtime/v1/:projectId
# 1. Authenticate (must be first message within 10s)
> {"type":"auth","access_token":"eyJhbGci..."}
< {"type":"auth","status":"ok"}
# 2. Subscribe to table changes
> {"type":"subscribe","channel":"table:users"}
< {"type":"subscribed","channel":"table:users"}
# 3. Receive mutations when rows change
< {"type":"mutation","channel":"table:users","event":"INSERT","data":{"table":"users","operation":"INSERT","id":"1","timestamp":"..."}}
# 4. Unsubscribe
> {"type":"unsubscribe","channel":"table:users"}
< {"type":"unsubscribed","channel":"table:users"}{
"Messages": {
"auth": {
"type": "auth",
"status": "ok"
},
"subscribed": {
"type": "subscribed",
"channel": "table:users"
},
"mutation": {
"type": "mutation",
"channel": "table:users",
"event": "INSERT",
"data": {
"table": "users",
"operation": "INSERT",
"id": "1"
}
},
"error": {
"type": "error",
"message": "..."
}
}
}Auth Hub API
© 2026 Auth Hub Infrastructure