Skip to content

API Reference

RESTful API for YeboID authentication and identity management.

Base URLs

EnvironmentURL
Productionhttps://api.yeboid.com
Staginghttps://api-staging.yeboid.com

Authentication

Public Endpoints

No authentication required:

  • POST /auth/* (except logout)
  • GET /users/@:handle
  • GET /users/handle/check
  • GET /health

Protected Endpoints

Include the access token in the Authorization header:

http
Authorization: Bearer <access_token>

Request Format

http
Content-Type: application/json
Authorization: Bearer <token>
X-Request-ID: <uuid>   # Optional, for tracing

Response Format

Success

json
{
  "success": true,
  "data": { ... }
}

Error

json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable message",
    "details": { ... }
  }
}

Health

GET /health

Check API status.

Response:

json
{
  "status": "ok",
  "version": "1.0.0",
  "timestamp": "2026-03-18T20:00:00Z"
}

Authentication

POST /auth/otp/send

Send OTP to phone number.

Request:

json
{
  "phone": "+26878422613",
  "purpose": "signup"
}
FieldTypeRequiredDescription
phonestringYesE.164 format
purposestringYessignup or pin_reset

Response:

json
{
  "success": true,
  "data": {
    "expires_in": 300,
    "message": "OTP sent to +268****613"
  }
}

POST /auth/otp/verify

Verify OTP code.

Request:

json
{
  "phone": "+26878422613",
  "code": "123456",
  "purpose": "signup"
}

Response:

json
{
  "success": true,
  "data": {
    "verified": true,
    "temp_token": "eyJ...",
    "expires_in": 600
  }
}

POST /auth/signup

Create account after OTP verification.

Request:

json
{
  "temp_token": "eyJ...",
  "pin": "1234",
  "handle": "laslie",
  "name": "Laslie Georges Jr."
}
FieldTypeRequiredDescription
temp_tokenstringYesFrom OTP verify
pinstringYes4-6 digits
handlestringYes3-30 chars, lowercase
namestringNoDisplay name

Response:

json
{
  "success": true,
  "data": {
    "user": {
      "id": "uuid",
      "phone": "+26878422613",
      "handle": "laslie",
      "name": "Laslie Georges Jr.",
      "avatar_url": null,
      "kyc_status": "none",
      "created_at": "2026-03-18T20:00:00Z"
    },
    "access_token": "eyJ...",
    "refresh_token": "abc123...",
    "expires_in": 900
  }
}

POST /auth/signin

Sign in with phone and PIN.

Request:

json
{
  "phone": "+26878422613",
  "pin": "1234"
}

Response:

json
{
  "success": true,
  "data": {
    "user": { ... },
    "access_token": "eyJ...",
    "refresh_token": "abc123...",
    "expires_in": 900
  }
}

POST /auth/refresh

Refresh access token.

Request:

json
{
  "refresh_token": "abc123..."
}

Response:

json
{
  "success": true,
  "data": {
    "access_token": "eyJ...",
    "refresh_token": "def456...",
    "expires_in": 900
  }
}

Token Rotation

Refresh tokens are rotated on each use. The old token is invalidated.


POST /auth/logout

Revoke refresh token.

Headers: Authorization: Bearer <access_token>

Request:

json
{
  "refresh_token": "abc123..."
}

Users

GET /users/me

Get authenticated user's profile.

Headers: Authorization: Bearer <access_token>

Response:

json
{
  "success": true,
  "data": {
    "id": "uuid",
    "phone": "+26878422613",
    "phone_verified": true,
    "handle": "laslie",
    "name": "Laslie Georges Jr.",
    "avatar_url": "https://...",
    "bio": "Building the future of Africa",
    "country": "SZ",
    "language": "en",
    "kyc_status": "verified",
    "kyc_country": "SZ",
    "kyc_verified_at": "2026-03-18T20:00:00Z",
    "created_at": "2026-03-01T10:00:00Z",
    "updated_at": "2026-03-18T20:00:00Z"
  }
}

PATCH /users/me

Update user profile.

Headers: Authorization: Bearer <access_token>

Request:

json
{
  "name": "Laslie G.",
  "bio": "CEO @ Omevision",
  "avatar_url": "https://...",
  "language": "en"
}

All fields optional. Only include fields to update.


GET /users/@:handle

Get public profile by handle.

Response:

json
{
  "success": true,
  "data": {
    "id": "uuid",
    "handle": "laslie",
    "name": "Laslie Georges Jr.",
    "avatar_url": "https://...",
    "bio": "Building the future of Africa",
    "kyc_status": "verified",
    "created_at": "2026-03-01T10:00:00Z"
  }
}

Privacy

Phone, country, and language are NOT included in public profiles.


GET /users/handle/check

Check handle availability.

Query: ?handle=<handle>

Response:

json
{
  "success": true,
  "data": {
    "handle": "newhandle",
    "available": true
  }
}

DELETE /users/me

Delete account permanently.

Headers: Authorization: Bearer <access_token>

Request:

json
{
  "pin": "1234",
  "confirmation": "DELETE MY ACCOUNT"
}

Irreversible

This action is irreversible. Data is hard-deleted after 30 days.


Sessions

GET /sessions

List active sessions.

Headers: Authorization: Bearer <access_token>

Response:

json
{
  "success": true,
  "data": {
    "sessions": [
      {
        "id": "uuid",
        "device_name": "iPhone 15 Pro",
        "platform": "ios",
        "ip_address": "102.xxx.xxx.xxx",
        "last_used_at": "2026-03-18T20:00:00Z",
        "created_at": "2026-03-15T10:00:00Z",
        "current": true
      }
    ],
    "total": 1
  }
}

DELETE /sessions/:id

Revoke a session.

Headers: Authorization: Bearer <access_token>


KYC

GET /kyc/status

Get KYC verification status.

Headers: Authorization: Bearer <access_token>

Response:

json
{
  "success": true,
  "data": {
    "status": "verified",
    "verified_at": "2026-03-18T20:00:00Z",
    "level": "standard",
    "documents": ["id_card"]
  }
}

POST /kyc/initiate

Start KYC verification.

Headers: Authorization: Bearer <access_token>

Response:

json
{
  "success": true,
  "data": {
    "verification_url": "https://verify.yeboid.com/session/abc123",
    "expires_in": 1800
  }
}

Redirect user to verification_url to complete KYC.


Error Codes

CodeHTTPDescription
INVALID_REQUEST400Malformed request
INVALID_PHONE400Invalid phone format
INVALID_PIN400PIN doesn't meet requirements
INVALID_HANDLE400Invalid handle format
INVALID_OTP400Wrong OTP code
OTP_EXPIRED400OTP has expired
INVALID_CREDENTIALS401Wrong phone or PIN
INVALID_TOKEN401Access token invalid
TOKEN_EXPIRED401Access token expired
INVALID_REFRESH_TOKEN401Refresh token invalid
ACCOUNT_LOCKED403Account temporarily locked
FORBIDDEN403Not allowed
NOT_FOUND404Resource not found
PHONE_NOT_FOUND404Phone not registered
HANDLE_TAKEN409Handle already in use
HANDLE_RESERVED409Handle is reserved
PHONE_EXISTS409Phone already registered
HANDLE_COOLDOWN429Must wait between handle changes
RATE_LIMITED429Too many requests
INTERNAL_ERROR500Server error

Rate Limits

EndpointLimitWindow
POST /auth/otp/send31 hour
POST /auth/signin515 minutes
POST /auth/otp/verify5per code
GET /users/handle/check301 minute
All other endpoints1001 minute

Response headers:

http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1710770000

SDKs

  • Flutter SDK — Official Flutter package
  • More SDKs coming soon

API Version: 1.0

Universal Authentication for Africa