A booking system API for Servas International, enabling new applicants to schedule interviews with regional interviewers.
While building a booking system for Servas International, I tried several open source scheduling tools. Every time, I hit the same wall — the features I needed were locked behind enterprise or paid editions. Open source shouldn't mean "free demo."
So I decided to build it myself, truly open source, with no paid tiers or locked features. If you need a simple, honest booking system — use it, fork it, make it yours.
Servas operates globally with interviewers organised by country and region. When a new applicant joins, they need to book an interview with an available interviewer in their area. This API powers that booking flow:
- Applicants visit a public page, see available slots in their region, and book an interview
- Interviewers set their own availability and manage bookings
- Admins manage countries, regions, and interviewer teams
- Supports 15 or 30 minute interviews (video via Jitsi or in-person)
- Email verification for applicants (no account required)
- Waitlist when no slots are available
- Ruby 3.4.5
- Rails 8.1.3 (API-only mode)
- PostgreSQL 17 (via Docker)
- RSpec for testing (55 tests)
- FactoryBot for test data
- JWT for authentication
- bcrypt for password encryption
- Ruby 3.4.5
- Bundler
- Docker
# Clone the repo
git clone https://github.com/makifyegin/fox-api.git
cd fox-api
# Install dependencies
bundle install
# Start PostgreSQL in Docker
docker run --name fox-db \
-e POSTGRES_USER=your_username \
-e POSTGRES_PASSWORD=your_password \
-e POSTGRES_DB=fox_api_development \
-p 5432:5432 -d postgres:17
# Create your environment file
cp .env.example .env
# Edit .env with your database credentials
# Create and seed the database
rails db:create
rails db:migrate
rails db:seed
# Start the server
rails serverCreate a .env file in the project root (see .env.example):
DATABASE_USERNAME=your_username
DATABASE_PASSWORD=your_password
DATABASE_HOST=localhost
DATABASE_PORT=5432
# Run all tests
rspec
# Run a specific test file
rspec spec/models/user_spec.rb
# Run request specs only
rspec spec/requests| Method | URL | Description |
|---|---|---|
| GET | /api/v1/countries |
List all 249 ISO countries |
| GET | /api/v1/countries/:country_code/regions |
List regions for a country |
| GET | /api/v1/regions/:region_id/availabilities |
View available interview slots for a region |
| POST | /api/v1/login |
Login and receive JWT token |
| POST | /api/v1/bookings |
Book an interview slot |
| Method | URL | Description |
|---|---|---|
| GET | /api/v1/profile |
Get current user info |
| GET | /api/v1/availabilities |
List my availabilities |
| POST | /api/v1/availabilities |
Create an availability slot |
| DELETE | /api/v1/availabilities/:id |
Delete an availability slot |
To access protected endpoints, include the JWT token in the Authorization header:
# Login to get a token
curl -X POST http://localhost:3000/api/v1/login \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "your_password"}'
# Use the token for protected endpoints
curl http://localhost:3000/api/v1/profile \
-H "Authorization: Bearer YOUR_TOKEN_HERE"# 1. Browse available slots for a region
curl http://localhost:3000/api/v1/regions/1/availabilities
# 2. Book a slot
curl -X POST http://localhost:3000/api/v1/bookings \
-H "Content-Type: application/json" \
-d '{
"availability_id": 1,
"start_time": "10:00",
"duration": 30,
"interview_type": "video",
"name": "Your Name",
"email": "your@email.com",
"booker_type": "applicant"
}'Country (249 ISO 3166 countries)
└── Region (e.g. London, Scotland)
├── User (interviewer or admin)
│ └── Availability (free time slots)
│ └── Booking (applicant picks a slot)
└── Booker (applicant or member)
└── Booking
- Country — ISO 3166 countries with 2-letter codes (GB, FR, DE)
- Region — geographical areas within a country, belongs to a country
- User — interviewers and admins who log in, belongs to a region
- Availability — time slots when an interviewer is free (date, start_time, end_time)
- Booker — applicants or members who book interviews, has a type (applicant/member)
- Booking — connects a booker to an availability slot (duration: 15/30 min, type: video/in_person, status: pending/confirmed/cancelled/completed/rescheduled)
🚧 Under active development
- Country model with ISO 3166 codes
- Region model with country association
- User model with roles (admin/interviewer)
- JWT authentication (login, protected endpoints)
- Profile endpoint
- Countries API endpoint
- Regions API endpoint
- Interviewer availability (create, list, delete)
- Booker model (applicant/member with conditional region)
- Booking model with validations
- Public availability viewing per region
- Public booking creation
- Interviewer booking management (view/confirm/cancel)
- Email verification for applicants
- Waitlist
- Rescheduling/cancellation
- Admin endpoints
- Swagger API documentation
- Jitsi video call integration
- Frontend (Next.js + coss ui)
This is an open source project. Contributions are welcome! Please open an issue or submit a pull request.