API documentation

    The Isokan African platform API is a JSON HTTP API. Every request is authenticated with a developer API key created in the developer portal.

    Manage my API keys

    API playground

    Run live requests against the posts endpoint with your own key. Scope checks, rate limits and audit logging apply exactly as they do from your server.

    Leave empty to see the exact 401 response an unauthenticated call returns.

    Equivalent curl

    curl "https://isokanafrican.com/api/public/v1/posts?limit=5" \
      -H "Authorization: Bearer isokan_xxxxxxxx"

    Base URLs

    All endpoints are versioned under /api/public/v1.

    Production: https://isokanafrican.com/api/public/v1

    Mirror: https://isokanafrican.lovable.app/api/public/v1

    Authorization

    Send your key as a bearer token on every request. Keys are shown once at creation — store them securely on your server and never in client-side code.

    Authorization: Bearer isokan_xxxxxxxxxxxxxxxxxxxxxxxx

    A missing, unknown or disabled key returns 401 with { "error": "Invalid or inactive API Key" }.

    Scopes

    Each key carries one or more scopes.

    read
    Read public posts and public user profiles.

    write
    Create posts on behalf of the key owner.

    analytics
    Reserved for upcoming analytics endpoints.

    Calling an endpoint without the required scope returns 403.

    Rate limits

    Limits are applied per API key.

    Default allowance: 60 requests per minute per key.

    Responses include x-ratelimit-limit and x-ratelimit-remaining. When the allowance is exhausted the API returns 429 with a retry-after header.

    HTTP/1.1 429 Too Many Requests
    retry-after: 60
    
    {
      "error": "Rate limit exceeded. This key allows 60 requests per minute.",
      "retry_after_seconds": 60
    }

    GET /posts

    Scope: read · Query: limit (1–50, default 20)

    curl "https://isokanafrican.com/api/public/v1/posts?limit=2" \
      -H "Authorization: Bearer isokan_xxxxxxxx"
    {
      "message": "Posts fetched successfully",
      "data": [
        {
          "id": "6f6c1f2e-...",
          "content": "Unity is strength #Isokan",
          "image_url": null,
          "image_urls": [],
          "video_url": null,
          "created_at": "2026-08-09T10:12:31.000Z"
        }
      ]
    }

    POST /posts

    Scope: write · Body: content (max 5000 chars)

    curl -X POST "https://isokanafrican.com/api/public/v1/posts" \
      -H "Authorization: Bearer isokan_xxxxxxxx" \
      -H "Content-Type: application/json" \
      -d '{"content":"Unity is strength #Isokan"}'
    HTTP/1.1 201 Created
    
    {
      "message": "Post created successfully",
      "data": {
        "id": "0b2b8f10-...",
        "content": "Unity is strength #Isokan",
        "created_at": "2026-08-09T10:14:02.000Z"
      }
    }

    GET /users/{id}

    Scope: read · id is the user UUID

    curl "https://isokanafrican.com/api/public/v1/users/00000000-0000-0000-0000-000000000000" \
      -H "Authorization: Bearer isokan_xxxxxxxx"
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "username": "ade",
      "name": "Ade Okafor",
      "avatar_url": "https://...",
      "bio": "Building in Lagos",
      "followers": 128
    }

    OAuth 2.0 for third-party apps

    Use OAuth when your app acts on behalf of an Isokan African member instead of your own account. Authorization code flow with PKCE; refresh tokens rotate on use.

    Register your application in the developer portal to get a client_id and a one-time client_secret, and declare your redirect URIs.

    Discovery document: https://isokanafrican.com/.well-known/oauth-authorization-server

    1. Send the member to the consent screen

    https://isokanafrican.com/oauth/authorize
      ?client_id=YOUR_CLIENT_ID
      &redirect_uri=https://yourapp.com/callback
      &response_type=code
      &scope=read%20profile
      &state=RANDOM_STATE
      &code_challenge=BASE64URL_S256_OF_VERIFIER
      &code_challenge_method=S256

    2. Exchange the code for tokens

    curl -X POST "https://isokanafrican.com/api/public/v1/oauth/token" \
      -H "Content-Type: application/json" \
      -d '{
        "grant_type": "authorization_code",
        "code": "AUTH_CODE",
        "client_id": "YOUR_CLIENT_ID",
        "client_secret": "YOUR_CLIENT_SECRET",
        "redirect_uri": "https://yourapp.com/callback",
        "code_verifier": "ORIGINAL_VERIFIER"
      }'
    {
      "access_token": "isokat_xxxxxxxx",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "isokrt_xxxxxxxx",
      "scope": "read profile"
    }

    3. Call the API with the access token

    curl "https://isokanafrican.com/api/public/v1/oauth/userinfo" \
      -H "Authorization: Bearer isokat_xxxxxxxx"

    Refresh and revoke

    # refresh (old refresh token is invalidated)
    curl -X POST "https://isokanafrican.com/api/public/v1/oauth/token" \
      -H "Content-Type: application/json" \
      -d '{"grant_type":"refresh_token","refresh_token":"isokrt_xxxxxxxx","client_id":"YOUR_CLIENT_ID","client_secret":"YOUR_CLIENT_SECRET"}'
    
    # revoke
    curl -X POST "https://isokanafrican.com/api/public/v1/oauth/revoke" \
      -H "Content-Type: application/json" \
      -d '{"token":"isokat_xxxxxxxx","client_id":"YOUR_CLIENT_ID","client_secret":"YOUR_CLIENT_SECRET"}'

    OAuth scopes: read, write, analytics and profile (basic account details). Members can revoke any connected app from Settings, which immediately invalidates its tokens.

    Errors

    Platform API errors always return the same envelope: a human-readable error plus a stable machine-readable code. Branch on code, never on the message text.

    {
      "error": "This credential does not have write permission",
      "code": "insufficient_scope",
      "required_scope": "write",
      "granted_scopes": ["read"]
    }

    400 invalid_request — malformed JSON or an invalid field (the offending field is returned as field).

    401 invalid_token — missing, unknown, disabled or expired API key / OAuth access token.

    403 insufficient_scope — the credential lacks the scope the endpoint requires.

    404 not_found — the requested resource does not exist or is not public.

    429 rate_limited — per-minute allowance exhausted; see retry_after_seconds and the retry-after header.

    500 server_error — unexpected failure; safe to retry with backoff.

    Common failures, verbatim

    # no key at all
    HTTP/1.1 401 Unauthorized
    {
      "error": "Invalid or inactive API key or access token. Send it as Authorization: Bearer <token> or x-api-key: <key>.",
      "code": "invalid_token"
    }
    
    # empty or oversized content on POST /posts
    HTTP/1.1 400 Bad Request
    { "error": "content is required and must be 1–5000 characters", "code": "invalid_request", "field": "content" }
    
    # too many calls
    HTTP/1.1 429 Too Many Requests
    retry-after: 60
    { "error": "Rate limit exceeded. This credential allows 60 requests per minute.", "code": "rate_limited", "retry_after_seconds": 60 }

    OAuth error responses

    The OAuth endpoints follow RFC 6749: an error code plus error_description.

    400 invalid_request — a required parameter is missing.

    400 invalid_grant — code or refresh token unknown, used, expired, revoked, issued to another client, redirect_uri mismatch, or PKCE verification failed.

    400 unsupported_grant_type — only authorization_code and refresh_token are supported.

    401 invalid_client — unknown client_id or wrong client_secret.

    401 invalid_token — access token expired or revoked by the member.

    403 insufficient_scope — the consent granted did not include the required scope.

    HTTP/1.1 400 Bad Request
    { "error": "invalid_grant", "error_description": "PKCE verification failed" }
    
    HTTP/1.1 401 Unauthorized
    { "error": "invalid_client", "error_description": "Unknown client_id" }
    
    HTTP/1.1 403 Forbidden
    { "error": "insufficient_scope", "error_description": "This access token was not granted the profile scope.", "required": "profile" }