API Reference

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
POST/api/v1/:projectId/registerBearer API Key

Register

Creates a new user account in the project. If the project has SMTP configured, a verification email is sent automatically.

Body Parameters

ParamTypeRequiredDescription
emailstringYesValid email address for the new account.
passwordstringYesPlain-text password (min 8 chars). Stored as bcrypt hash.
redirect_tostringNoRelative 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.
Request
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"
  }'
Response · 200 OK
{
  "message": "Registration successful. Check your email to confirm.",
  "user": {
    "id": "bc0ede1a-...",
    "email": "[email protected]"
  }
}
GET/api/v1/:projectId/verifyQuery Token

Verify 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

ParamTypeRequiredDescription
tokenstringYesOne-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.
Request
curl -X GET "https://your-domain.com/api/v1/:projectId/verify?token=eyJhbGci..."
Response · 200 OK
{
  "message": "Email confirmed successfully"
}
POST/api/v1/:projectId/loginBearer API Key

Login

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

ParamTypeRequiredDescription
emailstringYesRegistered email address.
passwordstringYesAccount 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.
Request
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"
  }'
Response · 200 OK
{
  "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": {}
  }
}
GET/api/v1/:projectId/userBearer JWT

Get 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.
Request
curl -X GET https://your-domain.com/api/v1/:projectId/user \
  -H "Authorization: Bearer <access_token>"
Response · 200 OK
{
  "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": {}
}
POST/api/v1/:projectId/refreshPublic

Refresh 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

ParamTypeRequiredDescription
refresh_tokenstringYesThe 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.
Request
curl -X POST https://your-domain.com/api/v1/:projectId/refresh \
  -H "Content-Type: application/json" \
  -d '{ "refresh_token": "a3f8c2d1e4b7..." }'
Response · 200 OK
{
  "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": {}
  }
}
POST/api/v1/:projectId/logoutBearer JWT

Logout

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

ParamTypeRequiredDescription
refresh_tokenstringNoIf 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.
Request
curl -X POST https://your-domain.com/api/v1/:projectId/logout \
  -H "Authorization: Bearer <access_token>"
Response · 200 OK
{
  "message": "Logged out successfully"
}
POST/api/v1/:projectId/recoverBearer API Key

Request 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

ParamTypeRequiredDescription
emailstringYesEmail 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.
Request
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]" }'
Response · 200 OK
{
  "message": "If the email exists, a recovery link will be sent"
}
POST/api/v1/:projectId/reset-passwordQuery Token

Reset Password

Sets a new password using the recovery token. The token is single-use and is invalidated after a successful reset.

Body Parameters

ParamTypeRequiredDescription
tokenstringYesOne-time recovery token from the reset email.
newPasswordstringYesNew plain-text password (min 8 chars).
Request
curl -X POST https://your-domain.com/api/v1/:projectId/reset-password \
  -H "Content-Type: application/json" \
  -d '{
    "token": "<recovery_token>",
    "newPassword": "newsecurepassword456"
  }'
Response · 200 OK
{
  "message": "Password updated successfully"
}

Cookie Session

3 endpoints
POST/api/v1/:projectId/sessionBearer API Key

Create 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

ParamTypeRequiredDescription
emailstringYesRegistered email address.
passwordstringYesAccount 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.
Request
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"
  }'
Response · 200 OK
{
  "user": {
    "id": "bc0ede1a-...",
    "email": "[email protected]",
    "confirmed_at": "2026-03-09T10:00:00Z",
    "app_metadata": {
      "provider": "email"
    },
    "user_metadata": {}
  }
}
GET/api/v1/:projectId/sessionCookie Session

Get 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.
Request
curl -X GET https://your-domain.com/api/v1/:projectId/session \
  -b cookies.txt
Response · 200 OK
{
  "user": {
    "id": "bc0ede1a-...",
    "email": "[email protected]",
    "confirmed_at": "2026-03-09T10:00:00Z",
    "app_metadata": {
      "provider": "email"
    },
    "user_metadata": {}
  }
}
DELETE/api/v1/:projectId/sessionCookie Session

Delete 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.
Request
curl -X DELETE https://your-domain.com/api/v1/:projectId/session \
  -b cookies.txt
Response · 200 OK
{
  "message": "Logged out successfully"
}

Database CRUD

4 endpoints
POST/api/v1/:projectId/db/createBearer API Key

Insert 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

ParamTypeRequiredDescription
tablestringYesName of the target table.
dataobjectYesColumns 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.
Request
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.
Response · 200 OK
{
  "data": {
    "rows": [
      {
        "id": 1,
        "name": "Alice",
        "email": "[email protected]"
      }
    ]
  }
}
POST/api/v1/:projectId/db/readBearer API Key

Query Rows

Queries rows from a table with field selection, advanced filtering with comparison operators, multi-column ordering, and pagination.

Body Parameters

ParamTypeRequiredDescription
tablestringYesName of the table to query.
selectstring[]NoArray of column names to return. Defaults to all columns ("*"). Example: ["id", "name", "price"]
whereobjectNoFilter conditions. Supports simple equality { "col": value } or operator syntax { "col": { "$gt": 10 } }. All conditions are ANDed. See operators below.
limitnumberNoMax rows to return. Defaults to 100, hard capped at 1000.
offsetnumberNoNumber of rows to skip (for pagination). Defaults to 0.
orderByobject | object[]NoSort 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" } }.
Request
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
  }'
Response · 200 OK
{
  "data": {
    "rows": [
      {
        "id": 3,
        "name": "Widget Pro",
        "price": 29.99
      },
      {
        "id": 7,
        "name": "Widget Basic",
        "price": 12.5
      }
    ],
    "count": 2
  }
}
POST/api/v1/:projectId/db/updateBearer API Key

Update 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

ParamTypeRequiredDescription
tablestringYesName of the table to update.
dataobjectYesFields to update as key-value pairs. Must be non-empty.
whereobjectYesFilter 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 *.
Request
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 }
  }'
Response · 200 OK
{
  "data": {
    "updated": 1,
    "rows": [
      {
        "id": 1,
        "name": "Alice",
        "active": false
      }
    ]
  }
}
POST/api/v1/:projectId/db/deleteBearer API Key

Delete 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

ParamTypeRequiredDescription
tablestringYesName of the table to delete from.
whereobjectYesFilter 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 *.
Request
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 }
  }'
Response · 200 OK
{
  "data": {
    "deleted": 1,
    "rows": [
      {
        "id": 1,
        "name": "Alice",
        "active": false
      }
    ]
  }
}

Storage

7 endpoints
POST/api/v1/:projectId/storage/bucketsBearer API Key

Create Bucket

Creates a new storage bucket for the project. Buckets organize files and control access (public vs private) and upload limits.

Body Parameters

ParamTypeRequiredDescription
namestringYesBucket name. 2-63 chars, lowercase alphanumeric, dashes, underscores. Must start with a letter or number.
isPublicbooleanNoIf true, files can be downloaded without authentication. Default: false.
maxFileSizenumberNoMax file size in bytes for this bucket. Default: 0 (no limit). Set a positive value to enforce a per-file size cap.
allowedMimeTypesstringNoComma-separated MIME types. Supports wildcards: "image/*,application/pdf". Default: all types allowed.
Request
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
  }'
Response · 200 OK
{
  "data": {
    "id": "uuid",
    "name": "avatars",
    "isPublic": true,
    "maxFileSize": 5242880,
    "allowedMimeTypes": null,
    "createdAt": "2024-01-01T00:00:00.000Z"
  }
}
GET/api/v1/:projectId/storage/bucketsBearer API Key

List Buckets

Lists all storage buckets for the project, including object count per bucket.

Request
curl https://your-domain.com/api/v1/:projectId/storage/buckets \
  -H "Authorization: Bearer <api_key>"
Response · 200 OK
{
  "data": [
    {
      "id": "uuid",
      "name": "avatars",
      "isPublic": true,
      "maxFileSize": 5242880,
      "_count": {
        "objects": 12
      }
    },
    {
      "id": "uuid",
      "name": "documents",
      "isPublic": false,
      "maxFileSize": 10485760,
      "_count": {
        "objects": 3
      }
    }
  ]
}
DELETE/api/v1/:projectId/storage/bucketsBearer API Key

Delete Bucket

Deletes a bucket and ALL its files permanently. This cannot be undone.

Body Parameters

ParamTypeRequiredDescription
namestringYesName of the bucket to delete.
Request
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" }'
Response · 200 OK
{
  "data": {
    "deleted": true
  }
}
POST/api/v1/:projectId/storage/uploadBearer API Key

Upload 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

ParamTypeRequiredDescription
fileFileYesThe file to upload (multipart form field).
bucketstringYesTarget bucket name.
keystringYesObject 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.
Request
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"
Response · 200 OK
{
  "data": {
    "id": "uuid",
    "key": "users/user1.jpg",
    "size": 45231,
    "mimeType": "image/jpeg",
    "bucketId": "uuid",
    "createdAt": "2024-01-01T00:00:00.000Z"
  }
}
GET/api/v1/:projectId/storage/object/:bucket/:keyBearer API Key

Download 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).
Request
# 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>"
Response · 200 OK
{
  "(raw file bytes with Content-Type header)": ""
}
DELETE/api/v1/:projectId/storage/object/:bucket/:keyBearer API Key

Delete File

Deletes a file from a bucket by its key.

Notes

  • The URL path is: /storage/object/{bucketName}/{objectKey}.
Request
curl -X DELETE https://your-domain.com/api/v1/:projectId/storage/object/avatars/users/user1.jpg \
  -H "Authorization: Bearer <api_key>"
Response · 200 OK
{
  "data": {
    "deleted": true
  }
}
POST/api/v1/:projectId/storage/listBearer API Key

List Objects

Lists objects in a bucket with optional prefix filtering and pagination.

Body Parameters

ParamTypeRequiredDescription
bucketstringYesBucket name to list objects from.
prefixstringNoFilter objects by key prefix. Example: "users/" to list all objects under the users directory.
limitnumberNoMax objects to return. Default: 100, max: 1000.
offsetnumberNoNumber of objects to skip (for pagination). Default: 0.
Request
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
  }'
Response · 200 OK
{
  "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
GET/api/v1/:projectId/auth/:providerPublic

Start 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

ParamTypeRequiredDescription
redirect_tostringNoRelative 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.
Request
# 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
Response · 200 OK
{
  "(302 Redirect to provider consent screen)": ""
}
GET/api/v1/:projectId/auth/callbackPublic

OAuth 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

ParamTypeRequiredDescription
codestringYesAuthorization code from the OAuth provider.
statestringYesSigned 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.
Request
# 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");
Response · 200 OK
{
  "(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
POST(configured webhook URL)Public

Webhook 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

ParamTypeRequiredDescription
eventstringYesEvent name: user.registered, user.login, user.confirmed, user.oauth_login, password.reset, test.ping
dataobjectYesEvent payload containing relevant data (e.g. user id and email).
timestampstringYesISO 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.
Request
# 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)
  );
}
Response · 200 OK
{
  "event": "user.registered",
  "data": {
    "user": {
      "id": "bc0ede1a-...",
      "email": "[email protected]"
    }
  },
  "timestamp": "2026-03-13T10:00:00.000Z"
}

Realtime (WebSocket)

1 endpoints
GETws://host:4000/realtime/v1/:projectIdBearer JWT

WebSocket 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).
Request
# 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"}
Response · 200 OK
{
  "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

v1.1 · Live