Skip to content

Authentication & Profile ​

Sign-up and log-in flow ​

Sign-up and log-in are the same flow. The phone number identifies the account.

App                                   API
 │  POST /auth/otp {phone}             │
 │────────────────────────────────────▶│  (no SMS in challenge environment)
 │  204                                │
 │◀────────────────────────────────────│
 │  POST /auth/verify {phone, code:"000000", device}
 │────────────────────────────────────▶│  creates user on first login
 │  200 {accessToken, refreshToken, user, isNewUser}
 │◀────────────────────────────────────│
 │                                     │
 │  if user.profileComplete == false:  │
 │    GET /usernames/{u}/availability  │
 │    PATCH /me {displayName, username}│
 │────────────────────────────────────▶│
 │  200 User {profileComplete: true}   │
 │◀────────────────────────────────────│

POST /auth/otp ​

json
{ "phone": "+6580000001" }

Always 204, whether or not the account exists. Rate limit: 3 per phone per 10 minutes.

POST /auth/verify ​

json
{
  "phone": "+6580000001",
  "code": "000000",
  "device": { "platform": "ios", "name": "Ana's iPhone", "appVersion": "1.0 (12)" }
}

Response 200:

json
{
  "accessToken": "eyJhbGciOi…",
  "accessTokenExpiresAt": "2026-10-22T14:18:11.482Z",
  "refreshToken": "rt_9c2f…",
  "deviceId": "01JB8ZD…",
  "isNewUser": true,
  "user": {
    "id": "01JB8Z…",
    "phone": "+6580000001",
    "displayName": null,
    "username": null,
    "about": "",
    "avatarUrl": null,
    "profileComplete": false,
    "privacy": { "lastSeen": "friends" }
  }
}

Errors: 401 OTP_INVALID (wrong code), 429 RATE_LIMITED.

One account can be signed in on several devices. Each sign-in creates a device with its own tokens.

Profile completion ​

Until profileComplete is true, the API rejects friend, conversation, and message endpoints with 403 PROFILE_INCOMPLETE. Your app must show profile setup first.

GET /usernames/{username}/availability ​

No Authorization needed (only X-Client-Key).

json
{ "username": "ana_souza", "available": false, "reason": "taken" }

reason: taken, invalid (format), or reserved (for example echo, admin).

PATCH /me ​

json
{ "displayName": "Ana Souza", "username": "ana_souza", "about": "Available", "avatarMediaId": null }
FieldRules
displayName1–50 characters. Required to complete the profile.
username^[a-z][a-z0-9_]{2,19}$. Unique. Required to complete the profile. Can be changed later.
about0–140 characters.
avatarMediaIdMedia ID from an image upload (A2), or null to remove.

Errors: 409 USERNAME_TAKEN, 400 VALIDATION_FAILED.

Friends receive a user.updated event when the profile changes.

GET /me ​

Returns the current user, including profileComplete and privacy.

PATCH /me/privacy ​

json
{ "lastSeen": "friends" }

everyone, friends (default), or nobody. With nobody, others see neither "online" nor "last seen".

Token refresh ​

POST /auth/refresh ​

json
{ "refreshToken": "rt_9c2f…" }

Returns a new accessToken and a new refreshToken. Save both before using either.

Rules for the app:

  • Refresh when the access token expires in less than 60 seconds, or after a 401 UNAUTHENTICATED.
  • Allow only one refresh at a time. Other requests wait for it.
  • If refresh fails with 401, the session is over: clear local data and show the welcome screen.
  • A refresh token is single use. If an old one is reused (for example after a crash during refresh), all tokens of that device are revoked.

Log out ​

POST /auth/logout ​

Revokes the device's tokens and removes its push token. 204.

Delete account ​

DELETE /me ​

Required by App Store Guideline 5.1.1(v). Show a confirmation before calling it. 204.

What happens:

  • All devices are logged out. Tokens are revoked.
  • Friendships, friend requests, and blocks are removed.
  • The user leaves all groups.
  • In existing conversations, other members see the sender as "Deleted account" (isDeleted: true, displayName: "Deleted account", no avatar). Messages already sent remain visible to them.
  • The phone number and username become free again.

Questions? Reply to your invitation email.