REST API Reference

Complete reference for all ezAuth API endpoints. All endpoints are prefixed with /v1 unless noted.

Authentication

Endpoints use one of two auth modes:

User-scoped endpoints (with publishable key) also require Authorization: Bearer <access_token>.


Authentication Endpoints

POST /v1/challenges

POST /v1/challenges Publishable

Request a hashcash proof-of-work challenge (required before signup when HASHCASH_ENABLED=true).

Response

{
  "challenge": "random-hex-string",
  "difficulty": 5,
  "algorithm": "argon2id",
  "params": {
    "time_cost": 2,
    "memory_cost": 19456,
    "parallelism": 1,
    "hash_len": 32
  },
  "expires_in": 300
}

POST /v1/signups

POST /v1/signups Publishable

Register a new user. Sends a verification email.

Request Body

FieldTypeRequiredDescription
emailstringYesUser's email address
passwordstringNoPassword (if app has passwords enabled)
redirect_urlstringNoURL to redirect after email verification
hashcashobjectConditionalRequired when hashcash is enabled: { challenge, nonce }

Response 200

{ "status": "verification_sent", "user_id": "uuid" }

POST /v1/signins

POST /v1/signins Publishable

Sign in a user. Strategy determines the flow.

Request Body

FieldTypeRequiredDescription
emailstringYesUser's email address
passwordstringNoRequired for strategy: "password"
strategystringNo"magic_link" (default) or "password"
redirect_urlstringNoURL to redirect after magic link verification

Response (magic_link) 200

{ "status": "verification_sent" }

Response (password) 200

{
  "access_token": "eyJhbG...",
  "refresh_token": "rt_...",
  "user_id": "uuid",
  "session_id": "uuid"
}

POST /v1/verify-code

POST /v1/verify-code Publishable

Verify a 6-digit code sent to the user's email.

Request Body

FieldTypeRequired
emailstringYes
codestringYes

Response 200

{
  "access_token": "eyJhbG...",
  "refresh_token": "rt_...",
  "user_id": "uuid",
  "session_id": "uuid"
}

GET /v1/email/verify

GET /v1/email/verify?token=... Publishable

Verify an email via magic link token. Sets __session cookie and redirects to redirect_url.

GET /v1/me

GET /v1/me Publishable + Session

Get the currently authenticated user. Reads from __session cookie or Authorization: Bearer <jwt>.

Response 200

{
  "user_id": "uuid",
  "email": "[email protected]",
  "email_verified": true,
  "is_bot": false
}

POST /v1/tokens/session

POST /v1/tokens/session Publishable

Refresh an expired access token.

Request Body

{ "refresh_token": "rt_..." }

Response 200

{
  "access_token": "eyJhbG...",
  "refresh_token": "new_rt_...",
  "user_id": "uuid",
  "session_id": "uuid"
}

POST /v1/sessions/logout

POST /v1/sessions/logout Publishable + Session

Sign out the current session. Revokes the session and clears the cookie.

Response 200

{ "status": "logged_out" }

OAuth Endpoints

GET /v1/oauth/{provider}/authorize

GET /v1/oauth/{provider}/authorize?redirect_url=... Publishable

Get the OAuth authorization URL. Supported providers: google, apple.

Response 200

{ "authorization_url": "https://accounts.google.com/..." }

GET /v1/oauth/{provider}/callback

GET /v1/oauth/{provider}/callback

OAuth redirect callback. Handles code exchange, token verification, user upsert, and redirect. Used by Google.

POST /v1/oauth/{provider}/callback

POST /v1/oauth/{provider}/callback

OAuth callback for the form_post response mode. Used by Apple.

GET /v1/oauth/providers

GET /v1/oauth/providers Secret

List configured OAuth providers (secrets redacted).

PUT /v1/oauth/providers/{provider}

PUT /v1/oauth/providers/{provider} Secret

Configure an OAuth provider. Supported providers: google, apple.

DELETE /v1/oauth/providers/{provider}

DELETE /v1/oauth/providers/{provider} Secret

Remove an OAuth provider configuration.


SSO Endpoints

GET /v1/sso/bridge

GET /v1/sso/bridge?return_to=... Publishable + Session

Generate a one-time SSO token and redirect to the target domain. Token expires in 60 seconds.

POST /v1/sso/exchange

POST /v1/sso/exchange Publishable

Exchange a one-time SSO token for a session. Both apps must belong to the same tenant.

Request Body

{ "token": "sso_token_string" }

Bot Endpoints

POST /v1/bot/signup

POST /v1/bot/signup Publishable

Register a bot with a confirmed donation challenge and Ed25519 public key.

Request Body

FieldTypeDescription
challenge_idstringConfirmed challenge ID from confirmations.info
public_keystringBase64-encoded Ed25519 public key

POST /v1/bot/auth

POST /v1/bot/auth Publishable

Authenticate a bot by verifying an Ed25519 signature.

Request Body

FieldTypeDescription
bot_idstringThe bot's user ID
timestampintegerCurrent Unix timestamp (seconds)
signaturestringBase64-encoded Ed25519 signature of ezauth:bot_auth:{app_id}:{bot_id}:{timestamp}

User Management

GET /v1/users

GET /v1/users?limit=50&offset=0&email=... Secret

List users for this application.

Query Parameters

ParamTypeDefaultDescription
limitint50Max results per page
offsetint0Pagination offset
emailstringFilter by email (case-insensitive)

Response 200

{
  "users": [
    {
      "id": "uuid",
      "email": "[email protected]",
      "email_verified": true,
      "is_bot": false,
      "created_at": "2025-01-01T00:00:00Z",
      "updated_at": "2025-01-01T00:00:00Z"
    }
  ],
  "total": 1
}

POST /v1/users

POST /v1/users Secret

Create a user server-side (skips email verification).

Request Body

FieldTypeRequired
emailstringYes
passwordstringNo

GET /v1/users/{user_id}

GET /v1/users/{user_id} Secret

Get a specific user by ID.


Session Management

POST /v1/sessions/revoke

POST /v1/sessions/revoke?session_id=... Secret

Revoke a session by its ID.

POST /v1/sign_in_tokens

POST /v1/sign_in_tokens Secret

Create a short-lived sign-in token for server-to-server auth.

Request Body

FieldTypeDefaultDescription
user_idstringUser ID to create token for
expires_in_secondsint300Token lifetime in seconds, capped at MAX_SIGNIN_TOKEN_LIFETIME_SECONDS

Response 200

{
  "token": "eyJhbG...",
  "refresh_token": "rt_...",
  "user_id": "uuid",
  "session_id": "uuid",
  "expires_at": "2025-01-01T00:05:00Z"
}

Custom Tables

POST /v1/tables

POST /v1/tables Secret / Publishable + Session

Create a new custom table with optional column definitions.

Request Body

{
  "name": "contacts",
  "columns": [
    { "name": "name", "type": "text", "required": true },
    { "name": "age", "type": "int" }
  ]
}

GET /v1/tables

GET /v1/tables Secret / Publishable + Session

List all tables for this application.

GET /v1/tables/{table_id}

GET /v1/tables/{table_id}

Get a table with its column definitions.

DELETE /v1/tables/{table_id}

DELETE /v1/tables/{table_id}

Delete a table and all its columns and rows.

POST /v1/tables/{table_id}/columns

POST /v1/tables/{table_id}/columns

Add a column to a table.

Request Body

FieldTypeRequiredDescription
namestringYesColumn name
typestringYesColumn type: text, int, bool, float, date, json
requiredboolNoWhether the column is required (default: false)
default_valueanyNoDefault value for the column
positionintNoColumn position/order

PATCH /v1/tables/{table_id}/columns/{column_id}

PATCH /v1/tables/{table_id}/columns/{column_id}

Update a column's properties.

DELETE /v1/tables/{table_id}/columns/{column_id}

DELETE /v1/tables/{table_id}/columns/{column_id}

POST /v1/tables/{table_id}/rows

POST /v1/tables/{table_id}/rows

Insert a row.

Request Body

{
  "data": { "name": "Alice", "age": 30 },
  "user_id": "optional-uuid"  // for user-scoped rows
}

GET /v1/tables/{table_id}/rows/{row_id}

GET /v1/tables/{table_id}/rows/{row_id}

PATCH /v1/tables/{table_id}/rows/{row_id}

PATCH /v1/tables/{table_id}/rows/{row_id}

Partial update of a row's data.

DELETE /v1/tables/{table_id}/rows/{row_id}

DELETE /v1/tables/{table_id}/rows/{row_id}

POST /v1/tables/{table_id}/rows/query

POST /v1/tables/{table_id}/rows/query

Query rows with filtering, sorting, and pagination.

Request Body

{
  "filter": { "field": "age", "op": "gte", "value": 18 },
  "sort": { "field": "name", "dir": "asc" },
  "limit": 50,
  "cursor": null
}

GET /v1/tables/storage

GET /v1/tables/storage

Get table storage usage for this application.

Response 200

{ "used_bytes": 1048576, "limit_bytes": 104857600, "used_percent": 1.0 }

Object Storage

POST /v1/buckets

POST /v1/buckets

Create a storage bucket.

Request Body

{ "name": "avatars" }

GET /v1/buckets

GET /v1/buckets

List all buckets.

GET /v1/buckets/{bucket_id}

GET /v1/buckets/{bucket_id}

PATCH /v1/buckets/{bucket_id}

PATCH /v1/buckets/{bucket_id}

Update a bucket's size limits. Send either field on its own; omit a field to leave it unchanged, or send null to remove the limit.

Request Body

FieldTypeDescription
max_size_bytesint | nullTotal size cap for the bucket
max_size_bytes_per_userint | nullPer-user size cap within the bucket

DELETE /v1/buckets/{bucket_id}

DELETE /v1/buckets/{bucket_id}

PUT /v1/buckets/{bucket_id}/objects/{key}

PUT /v1/buckets/{bucket_id}/objects/{key}?user_id=...

Upload an object. Send raw bytes with the appropriate Content-Type header.

GET /v1/buckets/{bucket_id}/objects/{key}

GET /v1/buckets/{bucket_id}/objects/{key}?user_id=...

Download an object. Returns raw bytes with the stored content type.

DELETE /v1/buckets/{bucket_id}/objects/{key}

DELETE /v1/buckets/{bucket_id}/objects/{key}?user_id=...

GET /v1/buckets/{bucket_id}/objects

GET /v1/buckets/{bucket_id}/objects?user_id=...&limit=50&cursor=...

List objects in a bucket.

GET /v1/buckets/storage

GET /v1/buckets/storage

Get object storage usage for this application.


Public Endpoints

GET /.well-known/jwks.json

GET /.well-known/jwks.json?app_id=... Public (no auth)

Returns the JSON Web Key Set for verifying JWTs. Resolves the application from the app_id query parameter or from the Host header (via domain lookup).

Response 200

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "key-id",
      "use": "sig",
      "alg": "RS256",
      "n": "...",
      "e": "AQAB"
    }
  ]
}

Billing

These tenant-scoped routes require an application's secret key and remain available while paused. See Billing for request and response examples.

MethodEndpointDescription
GET/v1/billingBalance, pause status, usage, and available payment methods.
POST/v1/billing/topupsCreate a top-up with amount_usd, method, and optional crypto chain.
GET/v1/billing/topups/{payment_id}Read status; crypto requests also check the provider and apply confirmed credit.
GET/v1/billing/transactions?limit=50Newest ledger rows (maximum limit 200).