Base URL: https://api.kaxan.com/v1
Kaxan API Reference

The Kaxan REST API lets you build on top of the full booking platform β€” manage tours, users, payments, and rewards programmatically.

πŸ”
JWT Auth
All protected endpoints use Bearer token authentication via JSON Web Tokens.
πŸ’³
Stripe Payments
Payments are processed via Stripe Payment Intents. PCI-compliant by design.
πŸ“¬
Email via SendGrid
Confirmation and transactional emails are sent automatically via SendGrid.
ℹ️
All API responses are JSON. All requests must include Content-Type: application/json. Dates use ISO 8601 format: YYYY-MM-DD.

πŸ” Authentication
How Authentication Works

After a successful login or register call, the API returns a signed JWT token. Include this token in the Authorization header of every protected request.

HTTP Header
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
⚠️
Admin-only endpoints additionally require the X-Admin-Key header. Store your admin key in environment variables β€” never expose it client-side.

Errors & Rate Limits

The API uses conventional HTTP status codes. Errors return a consistent JSON shape with a code and human-readable message.

Error Response
{
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "Email or password is incorrect.",
    "status": 401
  }
}
StatusMeaning
200OK β€” request succeeded
201Created β€” resource created
400Bad Request β€” invalid parameters
401Unauthorized β€” missing or invalid token
403Forbidden β€” insufficient permissions
404Not Found β€” resource doesn't exist
409Conflict β€” e.g. email already registered
429Too Many Requests β€” rate limit exceeded
500Server Error β€” contact support
⚑
Rate limit: 200 requests/min per IP for public endpoints. Authenticated users: 500 requests/min. Admin: unlimited. Limits reset every 60 seconds.

πŸ‘€ User Auth Endpoints
# auth / register
Register User

Creates a new user account. Returns a JWT token and the user object. A welcome email is sent automatically via SendGrid.

POST /auth/register
Request Body
FieldTypeRequiredDescription
namestringrequiredFull name of the user
emailstringrequiredValid email address. Must be unique.
passwordstringrequiredMin 6 characters. Stored as bcrypt hash.
Request
POST /auth/register
Content-Type: application/json

{
  "name": "Elena Vasquez",
  "email": "elena@email.com",
  "password": "securepass123"
}
Response 201
{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJ1c3JfMTIzIn0.abc",
  "user": {
    "id": "usr_abc123",
    "name": "Elena Vasquez",
    "email": "elena@email.com",
    "points": 0,
    "tier": "bronze",
    "createdAt": "2026-03-12T10:00:00Z"
  }
}
# auth / login
Login

Authenticates an existing user. Returns a JWT token valid for 7 days.

POST /auth/login
Request
{
  "email": "elena@email.com",
  "password": "securepass123"
}
Response 200
{
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "expiresAt": "2026-03-19T10:00:00Z",
  "user": { /* user object */ }
}
# auth / me
Get Current User

Returns the authenticated user's full profile including points balance and tier.

GET /auth/me πŸ”’ Auth required
Response 200
{
  "id": "usr_abc123",
  "name": "Elena Vasquez",
  "email": "elena@email.com",
  "points": 1840,
  "tier": "silver",
  "bookingsCount": 4,
  "totalSpent": 18400,
  "createdAt": "2026-01-10T08:30:00Z"
}
# auth / update
Update Profile

Updates the authenticated user's name or password. Email cannot be changed after registration.

PUT /auth/me πŸ”’ Auth required
FieldTypeRequiredDescription
namestringoptionalNew display name
currentPasswordstringoptionalRequired only when changing password
newPasswordstringoptionalMin 6 chars. Requires currentPassword

πŸ—ΊοΈ Tour Endpoints
# tours / list
List Tours

Returns a paginated list of all available tours. Supports filtering and sorting.

GET /tours
Query Parameters
ParamTypeDefaultDescription
typestringallhiking | wildlife | all
pageinteger1Page number for pagination
limitinteger20Results per page (max 100)
sortBystringcreatedAtprice | rating | createdAt
availablebooleanfalseFilter only tours with open spots
Response 200
{
  "data": [
    {
      "id": "tour_001",
      "name": "Torres del Paine Circuit Trek",
      "location": "Patagonia, Chile",
      "type": "hiking",
      "days": 10,
      "startTime": "07:00",
      "endTime": "18:00",
      "priceAdult": 2340,
      "priceChild": 1560,
      "rating": 4.9,
      "imageUrl": "https://...",
      "inclusions": ["Professional guide", "All meals"],
      "nextAvailable": "2026-04-15"
    }
  ],
  "pagination": {
    "page": 1, "limit": 20, "total": 6, "pages": 1
  }
}
# tours / availability
Update Tour Availability

Add, update, or remove departure dates and their available spots for a specific tour. Admin only.

PUT /tours/:tourId/availability πŸ”’ Admin
Request
{
  "availability": {
    "2026-04-15": 12,
    "2026-05-01": 8,
    "2026-06-15": 0  // 0 = sold out
  }
}

πŸ“‹ Booking Endpoints
# bookings / create
Create Booking

Creates a new booking. Requires a confirmed Stripe Payment Intent ID. Points are automatically calculated and credited to the user's account after payment. A confirmation email is sent to the traveler.

POST /bookings πŸ”’ Auth required
FieldTypeRequiredDescription
tourIdstringrequiredID of the tour to book
departureDatestringrequiredISO date, must match available slot
adultsintegerrequiredAges 13–65. Min 1.
childrenintegeroptionalAges 4–12. Defaults to 0.
paymentIntentIdstringrequiredConfirmed Stripe Payment Intent ID
redeemPointsintegeroptionalPoints to redeem (min 100, multiples of 100)
contactNamestringrequiredLead traveler's full name
contactPhonestringoptionalLead traveler's phone number
Response 201
{
  "bookingId": "bkg_xyz789",
  "status": "confirmed",
  "tourName": "Torres del Paine Circuit Trek",
  "departureDate": "2026-04-15",
  "startTime": "07:00",
  "endTime": "18:00",
  "adults": 2,
  "children": 1,
  "baseTotal": 6240,
  "pointsDiscount": 50,
  "totalCharged": 6190,
  "pointsEarned": 619,
  "confirmationEmail": "sent"
}

πŸ’³ Payment Endpoints
# payments / intent
Create Payment Intent

Creates a Stripe Payment Intent for a booking. Returns a clientSecret to complete the payment on the frontend using Stripe.js. Always call this before creating a booking.

POST /payments/intent πŸ”’ Auth required
FieldTypeRequiredDescription
tourIdstringrequiredTour being booked
adultsintegerrequiredNumber of adult travelers
childrenintegeroptionalNumber of child travelers
redeemPointsintegeroptionalPoints to deduct from total. Server validates available balance.
currencystringoptionalISO currency code. Default: usd
Response 200
{
  "paymentIntentId": "pi_3OxHY2...",
  "clientSecret": "pi_3OxHY2..._secret_abc",
  "amountCents": 619000,
  "currency": "usd",
  "breakdown": {
    "baseTotal": 6240,
    "pointsDiscount": 50,
    "finalTotal": 6190
  }
}
ℹ️
Use the clientSecret with stripe.confirmCardPayment() on the frontend. Once confirmed, pass the paymentIntentId to POST /bookings.
# payments / refund
Issue Refund

Initiates a full or partial refund via Stripe. Points earned from this booking are deducted from the user's balance. Admin only.

POST /payments/:paymentIntentId/refund πŸ”’ Admin
FieldTypeRequiredDescription
amountintegeroptionalRefund amount in cents. Omit for full refund.
reasonstringoptionalduplicate | fraudulent | requested_by_customer

🌟 Points & Rewards Endpoints
# points / balance
Get Points Balance

Returns the current authenticated user's points balance, tier, and redeemable value.

GET /points/balance πŸ”’ Auth required
Response 200
{
  "points": 1840,
  "tier": "silver",
  "redeemableValue": 184.00,
  "nextTier": "gold",
  "pointsToNextTier": 160,
  "ratePerDollar": 0.1,
  "minRedeem": 100
}
# points / history
Points History

Returns a paginated list of all points transactions for the authenticated user.

GET /points/history πŸ”’ Auth required
Response 200
{
  "transactions": [
    {
      "type": "earned",
      "amount": +619,
      "description": "Torres del Paine booking",
      "bookingId": "bkg_xyz789",
      "createdAt": "2026-03-12T10:00:00Z"
    },
    {
      "type": "redeemed",
      "amount": -500,
      "description": "$50 discount on Masai Mara booking",
      "bookingId": "bkg_abc456",
      "createdAt": "2026-02-20T14:30:00Z"
    }
  ],
  "pagination": { "page": 1, "total": 8 }
}

βš™οΈ Admin Endpoints
🚨
All admin endpoints require both a valid JWT token AND the X-Admin-Key header. Never expose the admin key in frontend code. Always call admin routes server-side.
# admin / stats
Dashboard Stats

Returns aggregate statistics for the admin dashboard: revenue, bookings, new users, and top tours.

GET /admin/stats πŸ”’ Admin
Response 200
{
  "revenue": {
    "total": 284600,
    "thisMonth": 42300,
    "growth": "+18%"
  },
  "bookings": {
    "total": 340,
    "thisMonth": 24,
    "cancelled": 12
  },
  "users": {
    "total": 1204,
    "newThisMonth": 87
  },
  "topTours": [
    { "id": "tour_003", "name": "Everest Base Camp", "bookings": 62 }
  ]
}
# admin / emails
Send Email

Sends a transactional or broadcast email via SendGrid. Use for confirmations, reminders, or marketing campaigns.

POST /admin/emails/send πŸ”’ Admin
FieldTypeRequiredDescription
tostring | string[]requiredRecipient email(s)
templatestringrequiredbooking_confirmation | welcome | cancellation | reminder | custom
subjectstringoptionalRequired when template is custom
dataobjectoptionalTemplate variables (e.g. {"name": "Elena", "tourName": "..."})

Webhooks

Kaxan can push real-time events to your server via webhooks. Register a URL in the admin dashboard and we'll POST a signed payload for each event.

Available Events
EventTrigger
booking.createdA new booking is confirmed and paid
booking.cancelledA booking is cancelled
payment.succeededStripe payment is captured
payment.refundedA refund is issued
user.registeredA new user account is created
points.redeemedA user redeems points on a booking
πŸ”’
All webhook payloads include an X-Kaxan-Signature header. Verify it using your webhook secret to authenticate requests: HMAC-SHA256(payload, secret).
Example Webhook Payload
{
  "event": "booking.created",
  "timestamp": "2026-03-12T10:00:00Z",
  "data": {
    "bookingId": "bkg_xyz789",
    "userId": "usr_abc123",
    "tourId": "tour_001",
    "totalCharged": 6190,
    "pointsEarned": 619
  }
}