All systems operational · v1.0

One Click Spaces API

Everything you need to build on top of Africa's bookable spaces marketplace — villas, apartments, venues and studios, all behind one REST API.

View API Reference PRun in Postman
Getting Started

Authentication#

All API requests (except public endpoints) require a Bearer token in the Authorization header.

httphttp
Authorization: Bearer <access_token>

Tokens are obtained from POST /api/auth/login/. Access tokens expire in 60 minutes. Use the refresh endpoint with your refresh token to get a new one.

EnvironmentBase URL
Productionhttps://api.oneclickspaces.com
Local devhttp://127.0.0.1:8000

Every response — success or error — uses the same envelope.

jsonjson
{
  "success": true,
  "message": "Listings retrieved successfully.",
  "data": { },
  "errors": null
}

On error, success is false, data is null, and errors contains field-level validation errors.

StatusMeaning
200Success
201Created
400Validation error (check errors field)
401Missing or invalid token
403Insufficient permissions
404Resource not found
429Rate limit exceeded
500Server error
  • • Anonymous: 30 requests/minute
  • • Authenticated: 200 requests/minute
  • • Headers returned: X-RateLimit-Limit, X-RateLimit-Remaining

Spin up the entire API in seconds. Import the collection and environment below into Postman, set base_url (defaults to production), then run Login — the test script auto-populates access_token and refresh_token for every subsequent request.

  1. 1. In Postman, click Import and drop both files.
  2. 2. Select the One Click Spaces — API environment in the top-right picker.
  3. 3. Run Authentication → Login with valid credentials.
  4. 4. Every authenticated request now uses the captured bearer token automatically.
API Reference

Auth#

Register, login, verify, refresh, and manage user profiles.

POST/api/auth/register/

Creates a new user account and sends an OTP to the provided email. If a phone number is supplied, a second OTP is sent via SMS.

Request body

jsonjson
{
  "email": "john@example.com",
  "password": "SecurePass@123",
  "first_name": "John",
  "last_name": "Doe",
  "phone_number": "+255712345678"
}

Response · 201

jsonjson
{
  "success": true,
  "message": "Registration successful. Please verify your email.",
  "data": {
    "user_id": "a1b2c3d4-...",
    "email": "john@example.com"
  },
  "errors": null
}
OTP is sent to email immediately. Phone OTP sent via SMS if phone_number provided.
POST/api/auth/login/

Authenticate with email and password. Returns JWT access + refresh tokens.

Request body

jsonjson
{
  "email": "john@example.com",
  "password": "SecurePass@123"
}

Response · 200

jsonjson
{
  "success": true,
  "message": "Login successful.",
  "data": {
    "access": "<jwt_access_token>",
    "refresh": "<jwt_refresh_token>",
    "user": {
      "id": "a1b2c3d4-...",
      "email": "john@example.com",
      "first_name": "John",
      "role": "user",
      "mode": "booking",
      "verification_stage": "approved"
    }
  },
  "errors": null
}
POST/api/auth/logout/

Blacklists the supplied refresh token.

Request body

jsonjson
{ "refresh": "<refresh_token>" }
POST/api/auth/refresh/

Exchange a refresh token for a new short-lived access token.

Request body

jsonjson
{ "refresh": "<refresh_token>" }
POST/api/auth/verify-email/

Verify the 6-digit OTP sent to the user's email.

Request body

jsonjson
{
  "email": "john@example.com",
  "otp_code": "123456"
}
POST/api/auth/verify-phone/

Verify the 6-digit OTP sent via SMS.

Request body

jsonjson
{
  "phone_number": "+255712345678",
  "otp_code": "123456"
}
POST/api/auth/resend-otp/

Resend a one-time password. purpose can be email_verification, phone_verification, or password_reset.

Request body

jsonjson
{
  "email": "john@example.com",
  "purpose": "email_verification"
}
POST/api/auth/forgot-password/

Send a password reset OTP to the user's email.

Request body

jsonjson
{ "email": "john@example.com" }
POST/api/auth/reset-password/

Reset password using the OTP from forgot-password.

Request body

jsonjson
{
  "email": "john@example.com",
  "otp_code": "123456",
  "new_password": "NewSecurePass@123"
}

Get profile#

🔒 Auth required
GET/api/auth/profile/

Returns the authenticated user's full profile.

Update profile#

🔒 Auth required
PUT/api/auth/update-profile/

Update basic profile fields (name, phone, bio).

Request body

jsonjson
{
  "first_name": "John",
  "last_name": "Doe",
  "phone_number": "+255712345678",
  "bio": "Hospitality enthusiast."
}

Toggle UI mode#

🔒 Auth required
PATCH/api/auth/mode/

Switch the user between hosting and booking experiences. Does not affect API permissions.

Request body

jsonjson
{ "mode": "hosting" }
mode options: hosting · booking

Change password#

🔒 Auth required
POST/api/auth/change-password/

Request body

jsonjson
{
  "old_password": "OldPass@123",
  "new_password": "NewPass@123"
}

Host Onboarding#

Users must complete onboarding before they can create listings. Steps are tracked server-side.

Host status#

🔒 Auth required
GET/api/auth/host/status/

Returns the host onboarding checklist.

Response · 200

jsonjson
{
  "success": true,
  "data": {
    "personal_info": true,
    "identity": false,
    "profile_submitted": false,
    "verification_stage": "not_started"
  }
}

Save personal info#

🔒 Auth required
PATCH/api/auth/host/personal/

Save name, phone, date of birth and gender.

Request body

jsonjson
{
  "first_name": "John",
  "last_name": "Doe",
  "date_of_birth": "1992-03-14",
  "gender": "male",
  "phone_number": "+255712345678"
}

Upload identity#

🔒 Auth required
PATCH/api/auth/host/identity/

Upload NIDA number and ID photo as multipart/form-data.

Headers

Content-Typemultipart/form-data

Request body

jsonjson
{
  "nida_number": "19920314-12345-67890-12",
  "id_photo": "<binary>"
}

Submit profile for review#

🔒 Auth required
POST/api/auth/host/submit/

Submit the completed host profile for admin review. Sets verification_stage=pending_review.

OAuth / Providers#

Social login providers. Returns the same JWT response shape as standard login.

POST/api/auth/providers/google/

Pass the Google ID token obtained from the frontend SDK. Creates an account on first use.

Request body

jsonjson
{ "id_token": "<google_id_token>" }
POST/api/auth/providers/apple/

Returns 501 Not Implemented. Coming soon.

Listings#

Browse endpoints are public. Create / edit / delete require an approved host.

GET/api/listings/

Public. Supports the query parameters below.

ParamTypeDescription
categorystringCategory slug (e.g. beach-villa)
min_pricenumberMinimum price per night
max_pricenumberMaximum price per night
guestsnumberMinimum guest capacity
stay_typestringshort_stay | long_stay | both
check_indateYYYY-MM-DD
check_outdateYYYY-MM-DD
GET/api/listings/nearby/?lat=-6.79&lng=39.20&radius=10

Public. Returns listings within radius (km) of the supplied coordinates.

Create a listing#

🔒 Host required
POST/api/listings/

Create a draft listing. Must be an approved host.

Request body

jsonjson
{
  "title": "Luxury Beach Villa",
  "property_type": "villa",
  "category": "beach-villa",
  "stay_type": "short_stay",
  "description": "Stunning 3-bedroom beach villa with private pool.",
  "address": "Msasani Peninsula, Dar es Salaam",
  "city": "Dar es Salaam",
  "country": "Tanzania",
  "price_per_night": "350.00",
  "max_guests": 6,
  "bedrooms": 3,
  "bathrooms": 2
}
GET/api/listings/{listing_id}/

Public. Returns full detail including photos, amenities, pricing and reviews summary.

My listings (host)#

🔒 Auth required
GET/api/listings/host/

Returns the host's own listings across all statuses.

Update basic info#

🔒 Auth required
PATCH/api/listings/{listing_id}/basic-info/

Update title, address, guest count, etc.

Set amenities & extras#

🔒 Auth required
PATCH/api/listings/{listing_id}/amenities/

Update pricing#

🔒 Auth required
PATCH/api/listings/{listing_id}/pricing/

Update price, cleaning fee, security deposit.

Upload photos#

🔒 Auth required
POST/api/listings/{listing_id}/media/

multipart/form-data. Uploads photos to cloud storage.

Headers

Content-Typemultipart/form-data

Set availability#

🔒 Auth required
PATCH/api/listings/{listing_id}/availability/

Set check-in / out times and blackout dates.

Submit for review#

🔒 Auth required
POST/api/listings/{listing_id}/submit/

Submit the draft listing for admin approval.

Delete listing#

🔒 Auth required
DELETE/api/listings/{listing_id}/delete/

Delete a draft or inactive listing.

Bookings#

Booking flow: Create Intent → Confirm → Submit Payment → Admin Confirms → Check-In → Check-Out.

Create booking intent#

🔒 Auth required
POST/api/bookings/intents/

Places a 15-minute hold on the requested dates and returns a price snapshot.

Request body

jsonjson
{
  "listing_id": "abc-123",
  "check_in_date": "2026-09-01",
  "check_out_date": "2026-09-05",
  "guests": 2,
  "extras": []
}

Response · 201

jsonjson
{
  "success": true,
  "data": {
    "id": "intent-uuid",
    "expires_at": "2026-06-17T12:30:00Z",
    "price_snapshot": {
      "nights": 4,
      "price_per_night": "350.00",
      "subtotal": "1400.00",
      "cleaning_fee": "50.00",
      "total": "1450.00"
    }
  }
}
GET/api/bookings/intents/{intent_id}/

Check intent status and expiry.

Confirm booking#

🔒 Auth required
POST/api/bookings/create/

Confirms the intent and creates an actual booking.

Request body

jsonjson
{
  "intent_id": "intent-uuid",
  "special_requests": "Late check-in at 6pm please."
}

My bookings#

🔒 Auth required
GET/api/bookings/

Guests see their own bookings; hosts see bookings on their listings.

Booking detail#

🔒 Auth required
GET/api/bookings/{booking_id}/

Full detail including status history.

Submit payment proof#

🔒 Auth required
PATCH/api/bookings/{booking_id}/submit-payment/

Upload payment proof as multipart/form-data.

Headers

Content-Typemultipart/form-data

Cancel booking#

🔒 Auth required
POST/api/bookings/{booking_id}/cancel/

Cancels the booking and releases the held dates.

PATCH/api/bookings/{booking_id}/check-in/

Host action. Marks the guest as checked in.

PATCH/api/bookings/{booking_id}/check-out/

Marks the stay as complete and triggers a review prompt to the guest.

Status flow: confirmedpayment_submittedconfirmedchecked_incompleted

Categories#

Hierarchical property categories with nested subcategories.

GET/api/categories/

Public. Returns the full tree with nested subcategories.

Response · 200

jsonjson
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "name": "Villa",
      "slug": "villa",
      "icon": "villa",
      "subcategories": [
        { "name": "Beach Villa", "slug": "beach-villa" },
        { "name": "Modern Villa", "slug": "modern-villa" }
      ]
    }
  ]
}
GET/api/categories/{slug}/

Public. Single category with children.

POST/api/categories/
PATCH/api/categories/{slug}/
DELETE/api/categories/{slug}/

Amenities & Extras#

Amenities included with a stay, plus optional paid extras.

GET/api/amenities/

Public. WiFi, Pool, Parking, etc.

GET/api/amenities/extras/

Public. Airport Transfer, Breakfast, etc.

Create amenity#

🔒 Admin
POST/api/amenities/

Update amenity#

🔒 Admin
PATCH/api/amenities/{id}/

Delete amenity#

🔒 Admin
DELETE/api/amenities/{id}/
Same CRUD pattern applies to /api/amenities/extras/{id}/.

Reviews#

Listing reviews include sub-ratings and host replies.

GET/api/reviews/listings/{listing_id}/

Public. Published reviews with sub-ratings and host replies.

Submit a review#

🔒 Auth required
POST/api/reviews/listings/{listing_id}/

Submit a review for a completed booking. One review per booking.

Request body

jsonjson
{
  "booking_id": "booking-uuid",
  "rating": 5,
  "cleanliness": 5,
  "communication": 5,
  "location": 4,
  "value": 4,
  "accuracy": 5,
  "comment": "Absolutely stunning villa. Would book again!"
}
One review per booking. Must be the customer on that booking.

Host reply#

🔒 Host
PATCH/api/reviews/{review_id}/reply/

Post a one-time reply to a review.

Request body

jsonjson
{ "host_reply": "Thank you so much! Come back anytime!" }

Platform feedback#

🔒 Auth required
POST/api/reviews/app/

Internal platform feedback — not displayed publicly.

Request body

jsonjson
{
  "rating": 5,
  "comment": "Seamless booking experience!",
  "platform": "web"
}

Saved Searches#

Let users save and recall their favourite search filters.

List saved searches#

🔒 Auth required
GET/api/filters/saved/

Save a search#

🔒 Auth required
POST/api/filters/saved/

Request body

jsonjson
{
  "name": "Dar Beach Getaway",
  "category": "beach-villa",
  "min_price": 100,
  "max_price": 400,
  "min_guests": 2,
  "stay_type": "short_stay"
}

Remove saved search#

🔒 Auth required
DELETE/api/filters/saved/{id}/

FAQ#

Help-center content grouped by category.

GET/api/faq/

Public. Returns all active FAQs grouped by category.

Admin Portal#

All /api/admin/ endpoints require role=admin or is_superuser=true.

GET/api/admin/stats/

All listings#

🔒 Admin
GET/api/admin/listings/?status=pending_review

Returns listings across all statuses with optional filters.

POST/api/admin/listings/

Admin creates a platform-owned listing.

PATCH/api/admin/listings/{id}/approve/

Approves a listing — status becomes active.

Reject listing#

🔒 Admin
PATCH/api/admin/listings/{id}/reject/

Rejects a listing with a reason — host is notified.

Request body

jsonjson
{ "rejection_reason": "Photos too low quality." }

List users#

🔒 Admin
GET/api/admin/users/?role=user&verification_stage=pending_review

All users with optional filters.

Update user#

🔒 Admin
PATCH/api/admin/users/{id}/

Update role or active status.

Approve host#

🔒 Admin
POST/api/admin/users/{id}/approve-host/

Reject host#

🔒 Admin
POST/api/admin/users/{id}/reject-host/

Request body

jsonjson
{ "rejection_reason": "Identity document unclear." }

List bookings#

🔒 Admin
GET/api/admin/bookings/?status=payment_submitted

All bookings with optional filters.

PATCH/api/admin/bookings/{id}/confirm-payment/

Confirms payment — booking moves to confirmed.

PATCH/api/admin/reviews/{id}/unpublish/

Hide a review. Recalculates listing rating.

SMS balance#

🔒 Admin
GET/api/admin/sms/balance/

Current SendAfrica SMS credit balance.

Guide

Host Your First Space#

  1. 1
    Register and verify your email.
  2. 2
    Complete the host profile — personal info + NIDA identity.
  3. 3
    Submit your profile for admin review via POST /api/auth/host/submit/.
  4. 4
    Wait for admin approval (verification_stage=approved).
  5. 5
    Create a listing draft (POST /api/listings/).
  6. 6
    Add amenities, pricing, photos and availability.
  7. 7
    Submit the listing for review (POST /api/listings/{id}/submit/).
  8. 8
    Listing goes live after admin approval.
Guide

Complete a Booking#

  1. 1
    Browse listings (GET /api/listings/).
  2. 2
    Pick dates and create an intent (POST /api/bookings/intents/) — 15-minute hold begins.
  3. 3
    Review the price snapshot from the intent response.
  4. 4
    Confirm the booking (POST /api/bookings/create/).
  5. 5
    Upload payment proof (PATCH /api/bookings/{id}/submit-payment/).
  6. 6
    Admin confirms payment — booking is confirmed.
  7. 7
    Show up on the check-in date.
  8. 8
    After checkout, receive a review prompt via email / SMS.
Guide

Admin Workflow#

  1. 1
    Login as admin.
  2. 2
    Review pending host applications (GET /api/admin/users/?verification_stage=pending_review).
  3. 3
    Approve or reject hosts.
  4. 4
    Review pending listings (GET /api/admin/listings/?status=pending_review).
  5. 5
    Approve or reject listings with a reason.
  6. 6
    Monitor bookings and confirm payments.
  7. 7
    Check SMS balance (GET /api/admin/sms/balance/).