Skip to Content

Account

Manage the authenticated platform user — profile, password, email, and account deletion. All endpoints below require Authorization: Bearer TOKEN unless explicitly marked Public.

These endpoints manage your own Ghayma account (the developer using the platform). For managing the end-users of apps you’ve built on top of Auth Apps, see Auth Apps. For the public sign-in endpoints (/auth/register, /auth/login, OAuth), see the Auth Integration Guide.

Get Current User

GET /api/v1/auth/me

Returns the authenticated user. When an email change is pending, the response also includes a pending_email_change field so the dashboard can render a confirmation banner.

Response: 200

{ "id": "u_abc123", "email": "[email protected]", "name": "Alice", "email_verified": true, "avatar_url": "https://...", "auth_provider": "email", "password_updated_at": "2026-04-12T10:14:22Z", "role": "user", "created_at": "2026-01-03T08:00:00Z", "pending_email_change": { "new_email": "[email protected]", "expires_at": "2026-05-13T16:00:00Z" } }

pending_email_change is omitted when there is no active request — check for its presence rather than expecting a null. auth_provider is "email", "google", or "github".

Update Profile

PATCH /api/v1/auth/me

Edits the caller’s own profile. Only name is mutable today; avatar_url is OAuth-sourced and read-only here.

Body:

{ "name": "Alice Cooper" }
FieldTypeConstraints
namestringMax 100 characters; trimmed

Returns the updated user object (same shape as GET /auth/me, without pending_email_change).

Change Password

POST /api/v1/auth/change-password

Rotates the password for an email-provider account. Rate-limited to 5 requests per minute per IP. On success, a security notice is sent to the current email address and password_updated_at is stamped.

Body:

{ "current_password": "oldsecret123", "new_password": "newsecret456" }
FieldTypeConstraints
current_passwordstringRequired
new_passwordstringRequired, minimum 8 characters

Response: 200

{ "message": "password updated" }

Errors

StatuserrorWhen
400password must be at least 8 charactersNew password too short
400new password must differ from currentNew password equals current
401current password incorrectcurrent_password does not match

OAuth-only accounts (Google, GitHub) do not have a local password and cannot use this endpoint.

Email Change

Changing email is a three-step, double-confirmed flow designed to make silent account takeover infeasible:

  1. The user submits the new address + current password → confirmation link is emailed to the new address; a heads-up notice is emailed to the old address.
  2. The user clicks the link (on any device, signed in or not) → the change is applied atomically.
  3. A second notice is sent to the old address after the swap. The pending request expires after 1 hour if not confirmed.

OAuth-linked accounts (auth_provider ≠ "email") cannot use this flow — their canonical email is pinned to the upstream provider and must be changed there.

Request Email Change

POST /api/v1/auth/email/change-request

Initiates a pending change. Rate-limited to 3 requests per hour per IP. Any prior pending request for the same user is marked consumed before the new one is issued, so there is always at most one live token per account.

Body:

{ "new_email": "[email protected]", "current_password": "secret123" }

Response: 200

{ "message": "Check your new email to confirm the change", "expires_at": "2026-05-13T16:00:00Z" }

Errors

StatuserrorWhen
400new email must differ from currentSame as the address already on file
400invalid email formatDoesn’t contain @
400this account uses google sign-in; change your email there firstOAuth-linked account (provider name varies)
400could not start email changeTarget is already taken by another account (generic by design — does not leak account existence)
401current password incorrectcurrent_password does not match

Confirm Email Change

POST /api/v1/auth/email/change-confirm

Public — no Authorization header required. The token from the confirmation email is the credential. This lets users confirm from a different device that isn’t signed in.

Body:

{ "token": "a1b2c3..." }

Response: 200

{ "message": "email updated", "email": "[email protected]" }

On success, the user row is updated atomically inside a transaction: email is swapped, email_verified is set to true (the click is proof of ownership), and the legacy verification_token is cleared. A final notice is sent to the old address.

Errors

StatuserrorWhen
400invalid or expired tokenToken unknown, already consumed, or past its 1-hour TTL
409that email is now taken; please request again with a different addressSomeone else registered the target address between request and confirm

Cancel Email Change

DELETE /api/v1/auth/email/change-request

Marks the caller’s pending email change as consumed. Idempotent — succeeds even if there is no pending request. The row is preserved (not hard-deleted) so the audit trail stays intact.

Response: 200

{ "message": "pending email change cancelled" }

Delete Account

DELETE /api/v1/auth/account

Terminal. Removes the user. Returns 409 if the account still has resources that block deletion (e.g. active projects, unpaid balance).

Credit left on the billing accounts you own is forfeited — it is never paid out. The first call refuses with 409 and code: balance_forfeit_required, listing the amounts in balances and total_milli_dzd; repeat it with a body to confirm:

{ "forfeit_balance": true }

A negative balance is never forfeited: it answers 409 with code: balance_negative until it is settled with support. See Billing, Cycles & Credits.

Response: 200

{ "message": "account deleted" }

Security Notifications

All notifications are sent best-effort over SMTP and never block the underlying request. The transactional templates set RFC 8058 one-click unsubscribe headers but are not marked Precedence: bulk.

TriggerSent toSubject
Email change requestedNew address”Confirm your new Ghayma email”
Email change requestedOld address”Email change requested on your Ghayma account”
Email change confirmedOld address”Your Ghayma email was changed”
Password changedCurrent address”Your Ghayma password was changed”

The notices to the old address exist so that an attacker who somehow obtained a valid session and rotated the email or password can’t move silently — the legitimate user still gets one last signal at the address they recognise.