9. Managing End Users

A law firm onboards new employees. Alex needs to manage their accounts.

The Users page shows every end-user registered in your project. From here you can create, edit, suspend, and delete accounts.

User management features

  • User list — searchable table with email/phone, provider badges, status, and creation date
  • Provider column — shows how each user signed up (email, google, github, linkedin, apple, phone). Users with multiple linked providers show all badges.
  • Phone-only users — users who sign up via SMS OTP appear with their phone number instead of email
  • Create user — manually add a user by email; the password is optional (without one they sign in by magic link or a password reset)
  • Edit user — update email, display name, or metadata
  • Mark confirmed — confirm a user's email address by hand (for users who existed before you turned on email confirmation, or can't receive the email)
  • Suspend / reactivate — temporarily block a user from signing in (revokes all refresh tokens)
  • Delete user — permanently remove a user account
  • Reset password — set a new password for a user directly (revokes all refresh tokens)
  • Metadata JSON — attach arbitrary JSON metadata to any user (e.g., role, department, permissions)

Account linking

Accounts are not linked automatically. If someone signs in with a social provider (Google, GitHub, …) using an email that already has an account, the sign-in is refused with account_exists_link_required: they sign in the way they signed up. Linking automatically would let whoever controls the provider account take over the existing one. Users who sign in with several providers show all of them in the Provider column.

Managing users from your server

Everything on the Users page is also an API, for importing users or managing them from your own backend. These are platform endpoints on https://api.eurobase.app/platform/projects/$PROJECT_ID/users, authenticated with a project token with the Admin role (Account → Personal Access Tokens → pick the project and Admin). Your project's public and secret keys (eb_pk_ / eb_sk_) can't call them, and there's no /v1/auth/admin API. Keep the token on your server, like the secret key.

RequestDoes
GET /users?search=&email=&limit=&offset=List users, newest first: {"users": [...], "total": n}. limit up to 200 (default 50). email finds exactly that address (any case); search matches part of the email, display name or id.
GET /users/{id}One user
POST /usersCreate a user: {"email", "password", "display_name", "metadata", "email_confirmed"}. Only email is required; the password is 8–72 bytes (characters, for plain ASCII), and email_confirmed defaults to true
POST /users/batchImport up to 500 users in one request — see below
PATCH /users/{id}Change email, display_name, metadata (replaces the whole object), or email_confirmed (true / false)
DELETE /users/{id}Delete the user (refused while a retention hold covers them)
POST / DELETE /users/{id}/suspendSuspend (revokes their refresh tokens) / reactivate
POST /users/{id}/reset-passwordSet a new password: {"password"} (revokes their refresh tokens)
GET /users/{id}/exportThe user's data (GDPR Art. 15)

Each user has id, email, phone (when set), display_name, providers, metadata, banned_at, last_sign_in_at, email_confirmed_at and created_at.

API=https://api.eurobase.app/platform/projects/$PROJECT_ID/users
AUTH="Authorization: Bearer $EUROBASE_PROJECT_TOKEN"   # eb_ptk_…, Admin role

# find a user by email (exact)
curl -G "$API" -H "$AUTH" --data-urlencode "email=ann@example.com"

# create one (no password: they sign in with a magic link)
curl -X POST "$API" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"email":"ann@example.com","display_name":"Ann","metadata":{"role":"member"}}'

# change their email
curl -X PATCH "$API/$USER_ID" -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"email":"ann@new.example.com"}'

# delete
curl -X DELETE "$API/$USER_ID" -H "$AUTH"

Importing existing members

POST /users/batch takes {"users": [...]}, up to 500 per request, in one transaction. Each user is {"email", "display_name", "metadata", "email_confirmed", "password_hash", "id"}; only email is required.

  • No password: the user signs in with a magic link (turn on Magic link under Auth → Settings; eb.auth.requestMagicLink(email)) or sets one with a password reset. Signing in with a social provider for the same email is refused (see Account linking).
  • password_hash: a bcrypt hash ($2a$ / $2b$ / $2y$, cost at most 12 — the format Supabase exports) — users keep their password. Plain passwords aren't accepted in a batch; use POST /users for those.
  • id: keep the user's existing UUID, so rows that reference it still match. Don't reuse the id of a user you deleted: rows that still carry it (without a foreign key) would belong to the new user.
  • email_confirmed defaults to true.

The response counts created, exists and invalid, and lists every user in input order with its status (and id or error). Users whose email or id already exists are left unchanged, so re-running an import is safe.

curl -X POST "$API/batch" -H "$AUTH" -H "Content-Type: application/json" -d '{"users": [
  {"email": "ann@example.com", "display_name": "Ann", "metadata": {"role": "member"}},
  {"email": "ben@example.com", "password_hash": "$2a$10$…"}
]}'
# → {"created": 2, "exists": 0, "invalid": 0, "results": [{"index": 0, "email": "ann@example.com", "status": "created", "id": "…"}, …]}

Coming from Supabase, the migration CLI's import supabase (in the next CLI release) does this for you, OAuth identities included.

End users are stored in a users platform table that's managed separately from your application tables. You won't see it in the Database view — it's accessed through the Users tab.