Psyter Tahoon (Shared) API - Detailed Structure¶
Project: Tahoon Shared API (.NET 8)
Technology: ASP.NET Core Web API 8.0, JWT Authentication, Entity Framework
Purpose: Third-party integration API for external organizations (Tahoon platform)
Last Updated: November 5, 2025
📁 Root Structure¶
Tahoon_API/
├── PsyterSharedAPI/ # Main API project
│ ├── Controllers/ # API controllers
│ ├── Data/ # Data layer
│ │ ├── Model/ # Request/Response models
│ │ └── Repositories/ # Data access layer
│ ├── Helpers/ # Utility classes
│ ├── ActionFilters/ # Custom attributes
│ ├── Extensions/ # Extension methods
│ ├── Program.cs # Application entry point
│ ├── appsettings.json # Configuration
│ ├── firebase-adminsdk.json # FCM config
│ └── PsyterSharedAPI.csproj # Project file
├── PsyterSharedAPI.sln # Solution file
├── azure-pipelines.yml # CI/CD pipeline
├── .gitignore # Git ignore rules
└── .git/ # Git repository
🎯 Project Overview¶
Purpose¶
Third-party integration API designed specifically for external organizations (like Tahoon) to:
- Integrate: Connect external systems to Psyter platform
- Register Users: Create patient accounts for partner organizations
- Book Sessions: Schedule therapy sessions programmatically
- Manage Bookings: Cancel/reschedule appointments
- Access Providers: Get available care providers and schedules
- Secure Access: Organization-based authentication and authorization
Key Differences from Main API¶
Main API vs Tahoon API:
| Feature | Main API | Tahoon API |
|---|---|---|
| Users | Direct patients/providers | Partner organizations |
| Authentication | User credentials | Organization API key + JWT |
| Authorization | User-level permissions | Organization-level access |
| Purpose | Full platform features | Booking & integration only |
| User Registration | Self-registration | Programmatic registration |
| Payment | Direct payment gateway | Charity/organization credits |
| Technology | .NET Framework 4.7.2 | .NET 8 |
Architecture¶
.NET 8 Web API:
- RESTful API: HTTP/HTTPS endpoints
- JWT Authentication: Token-based security
- Repository Pattern: Separation of concerns
- Dependency Injection: Built-in IoC container
- Swagger/OpenAPI: API documentation
- Action Filters: Request validation & security
📦 Dependencies¶
PsyterSharedAPI.csproj¶
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<!-- Firebase Push Notifications -->
<PackageReference Include="Google.Apis.FirebaseCloudMessaging.v1" Version="1.70.0.3813" />
<!-- JWT Authentication -->
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.18" />
<!-- JSON Serialization -->
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<!-- API Documentation -->
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<!-- Database Access -->
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
<!-- JWT Token Generation -->
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.13.0" />
</ItemGroup>
</Project>
Core Components¶
ASP.NET Core 8.0:
- Built-in dependency injection
- Middleware pipeline
- Minimal API support
- Enhanced performance
JWT Bearer Authentication:
- Token-based security
- Claims-based authorization
- Organization-level access control
Firebase Cloud Messaging:
- Push notifications for bookings
- Multi-platform support (Android/iOS)
Swagger/OpenAPI:
- Interactive API documentation
- Try-it-out functionality
- Authentication testing
⚙️ Configuration¶
appsettings.json¶
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"AppBasePath": "https://dvx.innotech-sa.com/Psyter/Master/TuhoonAPIs",
"Jwt": {
"Key": "7955BF0A-8507-4214-B494-8B2B5F1BF150",
"Issuer": "psyter.com",
"Audience": "psyter_client"
},
"ConnectionStrings": {
"PsyterDatabase": "[Encrypted]",
"SchedulingDatabase": "[Encrypted]"
},
"CommandTimeout": "30",
"SecuritySettings": {
"AESKey": "pH2uJjFtaE4n+Kk3QfCNrX4lZkZcPbqP5gVZAp1o0P8=",
"AESIV": "q6JxgUuToRnQmTnOKGh3Nw==",
"PassPhrase": "55C9F5A8-17B8-4783-ABB1-D9AC7C3CF9BB",
"Salt": "2GlHRj2MxxxC2Dnn",
"Vector": "8WhO95QaRasSxsfh",
"EncryptionPassword": "Password*1"
}
}
Security Settings¶
Encryption:
- AES Key/IV: For AES encryption
- PassPhrase: For secure hash generation
- Salt/Vector: For password hashing
- EncryptionPassword: API access password
JWT Configuration:
- Key: Symmetric key for token signing
- Issuer: psyter.com
- Audience: psyter_client
- Expiration: 24 hours
Database:
- Two connection strings (encrypted)
- PsyterDatabase: Main database
- SchedulingDatabase: Scheduling system
🔐 Authentication Flow¶
1. Obtain Access Token¶
Endpoint: POST /api/auth/token
Request:
POST /api/auth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
access_key=YOUR_ORGANIZATION_KEY&grant_type=password
Process:
1. Validate access_key from database
2. Check organization exists and is active
3. Generate JWT token with organization claims
4. Return access token
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 86400
}
Token Claims:
{
"sub": "123", // Organization ID
"OrganizationId": "123", // Organization ID
"SharedAPIKey": "KEY", // API key
"jti": "unique-guid", // Token ID
"iat": 1699200000, // Issued at
"exp": 1699286400 // Expires at
}
2. Use Token in API Calls¶
Authorization Header:
GET /api/careprovider/getcareproviderslistwithschedule HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Token Validation:
- Verify signature with JWT key
- Check expiration
- Extract organization ID
- Validate organization access
📡 API Controllers¶
Total Controllers: 5¶
- AuthController - Authentication
- UserController - User management
- CareProviderController - Provider listing
- SessionBookingController - Booking operations
- SecurityController - Encryption utilities (internal)
1️⃣ AuthController¶
Route: /api/auth
POST /api/auth/token¶
Purpose: Obtain JWT access token
Authentication: None (public endpoint)
Request:
Content-Type: application/x-www-form-urlencoded
access_key=YOUR_ORG_KEY&grant_type=password
Response:
{
"access_token": "eyJhbGciOiJIUzI1...",
"token_type": "bearer",
"expires_in": 86400
}
Error Responses:
- 401 Unauthorized - Invalid credentials
2️⃣ UserController¶
Route: /api/user
Authentication: Required ([Authorize])
POST /api/user/register¶
Purpose: Register new organization user (patient)
Security:
- JWT authentication required
- Secure hash validation ([ValidateSecureHash])
- Anti-XSS validation ([ValidateAntiXSS])
Request:
{
"ReferenceId": "EXT-12345",
"Name": "John Smith",
"DOB": "1990-05-15",
"GenderType": 1,
"SecureHash": "computed_hash"
}
SecureHash Calculation:
Hash = SHA256(ReferenceId + Name + DOB + GenderType + PassPhrase)
Process:
1. Validate secure hash
2. Extract organization ID from JWT token
3. Check if user already exists (by ReferenceId)
4. Create user account in Psyter system
5. Link user to organization
6. Return encrypted user ID
Response:
{
"Status": 1,
"Reason": "Success",
"Message": "User registered successfully",
"Data": {
"UserLoginInfoId": 123,
"UserId": "encrypted_user_id",
"ReferenceId": "EXT-12345",
"Name": "John Smith"
}
}
GET /api/user/getassessmentquestions¶
Purpose: Get screening questions for assessment
Response:
{
"Status": 1,
"Data": [
{
"ScreeningQuestionId": 1,
"Question": "How often do you feel anxious?",
"QuestionType": 1,
"Options": [
{"OptionId": 1, "OptionText": "Never"},
{"OptionId": 2, "OptionText": "Sometimes"},
{"OptionId": 3, "OptionText": "Often"},
{"OptionId": 4, "OptionText": "Always"}
]
}
]
}
POST /api/user/submituserassessmentquestions¶
Purpose: Submit user screening assessment
Request:
{
"UserId": "encrypted_user_id",
"ScreeningResult": "Moderate",
"UserScreeningAnswers": [
{
"ScreeningQuestionId": 1,
"CatScreeningQuetionOptionId": 3,
"AnswerText": null
}
]
}
3️⃣ CareProviderController¶
Route: /api/careprovider
Authentication: Required
POST /api/careprovider/getcareproviderslistwithschedule¶
Purpose: Get filtered list of providers with available schedules
Request:
{
"UserId": "encrypted_user_id",
"ScheduleDate": "2025-11-10",
"GMTTimeDiffrenceHours": 3,
"ApplyFilter": true,
"FilterCriteria": {
"GenderType": 2,
"SpecialityId": 5,
"LanguageId": 1,
"MinExperience": 5,
"MaxFees": 300
}
}
Process:
1. Validate organization and user (if provided)
2. Get available schedules for date
3. Apply filters (gender, specialty, language, etc.)
4. Calculate available time slots
5. Sort by availability and match criteria
Response:
{
"Status": 1,
"Data": {
"CareProvidersList": [
{
"UserLoginInfoId": 456,
"FullName": "Dr. Sarah Ahmed",
"FirstNamePLang": "Dr. Sarah",
"LastNamePLang": "Ahmed",
"GenderType": 2,
"Speciality": "Clinical Psychologist",
"Experience": 10,
"ConsultationFee": 250,
"ProfileImage": "/path/to/image.jpg",
"Rating": 4.8,
"TotalRatings": 120,
"Languages": ["Arabic", "English"],
"AvailableScheduleHoursList": [
{
"Hour": 10,
"IsAvailable": true,
"SlotDuration": 60
}
],
"AvailableSlotsList": [
{
"StartTime": "10:00",
"EndTime": "11:00",
"IsAvailable": true
}
]
}
]
}
}
POST /api/careprovider/getcareproviderschedule¶
Purpose: Get specific provider’s schedule for a date
Request:
{
"CareProviderId": "encrypted_provider_id",
"CareProviderId_Decrypted": 456,
"UserId": "encrypted_user_id",
"UserId_Decrypted": 123,
"ScheduleDate": "2025-11-10",
"GMTTimeDiffrenceHours": 3
}
Response:
{
"AvailableHoursList": [
{"Hour": 10, "IsAvailable": true},
{"Hour": 11, "IsAvailable": false},
{"Hour": 14, "IsAvailable": true}
],
"AvailableHoursSlots": [
{
"ServiceProviderId": 456,
"StartTime": "10:00",
"EndTime": "11:00",
"IsAvailable": true,
"AvailabilityId": 789
}
],
"SlotDurationSetting": {
"ServiceProviderId": 456,
"SlotDuration": 60,
"SlotDurationType": 1
}
}
POST /api/careprovider/getcareprovidersprofiledata¶
Purpose: Get detailed profile of care provider
Request:
{
"CareProviderId": "encrypted_provider_id"
}
Response:
{
"Status": 1,
"Data": {
"UserLoginInfoId": 456,
"FullName": "Dr. Sarah Ahmed",
"Bio": "Experienced clinical psychologist...",
"Education": ["PhD Psychology", "MSc Clinical Psychology"],
"Certifications": ["CBT Specialist", "EMDR Certified"],
"Experience": 10,
"Specialities": ["Anxiety", "Depression", "PTSD"],
"Languages": ["Arabic", "English"],
"ConsultationFee": 250,
"Rating": 4.8,
"TotalRatings": 120,
"Reviews": [...]
}
}
GET /api/careprovider/getcataloguedataforfilters¶
Purpose: Get filter options (specialties, languages, etc.)
Response:
{
"Status": 1,
"Data": {
"Specialities": [
{"Id": 1, "Name": "Clinical Psychology"},
{"Id": 2, "Name": "Marriage Counseling"}
],
"Languages": [
{"Id": 1, "Name": "Arabic"},
{"Id": 2, "Name": "English"}
],
"GenderTypes": [
{"Id": 1, "Name": "Male"},
{"Id": 2, "Name": "Female"}
]
}
}
4️⃣ SessionBookingController¶
Route: /api/sessionbooking
Authentication: Required
Security: Anti-XSS validation
POST /api/sessionbooking/booksession¶
Purpose: Book therapy session for organization user
Security:
- JWT authentication
- Secure hash validation
- Anti-XSS protection
Request:
{
"UserId": "encrypted_user_id",
"UserId_Decrypted": 123,
"CareProviderId": "encrypted_provider_id",
"CareProviderId_Decrypted": 456,
"SlotDate": "2025-11-10",
"SlotStartTime": "10:00",
"SlotEndTime": "11:00",
"SlotAvailabilityId": 789,
"ApplicationMultiSlotId": "encrypted_slot_id",
"ApplicationMultiSlotId_Decrypted": 1,
"CareProviderSlotDurationId": "encrypted_duration_id",
"CareProviderSlotDurationId_Decrypted": 5,
"CatCommunicationTypeId": 3,
"IsBookingFromMobile": false,
"BookingPlatformId": 3,
"ReferenceId": "EXT-12345",
"Name": "John Smith",
"DOB": "1990-05-15",
"GenderType": 1,
"SecureHash": "computed_hash"
}
Process:
1. User Validation:
- If UserId provided: Validate user belongs to organization
- If no UserId: Auto-register user with ReferenceId/Name/DOB
-
Slot Validation:
- Check slot availability
- Verify provider schedule
- Confirm time slot not booked -
Booking Creation:
- Create booking in scheduling system
- Generate VideoSDK meeting ID
- Set booking status to “Confirmed”
- No payment required (charity/organization credits) -
Notifications:
- Send FCM notification to provider
- Create booking confirmation
- Schedule reminders
Response:
{
"Status": 1,
"Reason": "Success",
"Message": "Session booked successfully",
"Data": {
"MeetingId": "abc-123-def-456",
"BookingId": "encrypted_booking_id"
}
}
Meeting ID:
- Generated via VideoSDK API
- Used for video call session
- Stored in database for reference
POST /api/sessionbooking/cancelbooking¶
Purpose: Cancel scheduled booking
Security:
- JWT authentication
- Secure hash validation
- Anti-XSS protection
Request:
{
"BookingId": "encrypted_booking_id",
"BookingId_Decrypted": 999,
"CareProviderId": "encrypted_provider_id",
"CareProviderId_Decrypted": 456,
"UserId": "encrypted_user_id",
"UserId_Decrypted": 123,
"SecureHash": "computed_hash"
}
Process:
1. Validate user and organization
2. Check booking exists and is cancellable
3. Update booking status to “Cancelled”
4. Refund organization credits
5. Notify provider of cancellation
6. Update schedule availability
Response:
{
"Status": 1,
"Reason": "Success",
"Message": "Booking cancelled successfully",
"Data": {
"RefundAmount": 0,
"BookingStatus": "Cancelled"
}
}
5️⃣ SecurityController¶
Route: /api/security
Purpose: Internal encryption/decryption utilities
Hidden from API documentation: [ApiExplorerSettings(IgnoreApi = true)]
POST /api/security/encryptText¶
Purpose: Encrypt plain text (for testing)
Authentication: Password required
Request:
{
"PlainText": "123",
"AuthPssword": "Password*1"
}
Response:
{
"EncryptedString": "CYmF%2FyAGJuky51QOyaxql%2FwHXg8ZJXcG..."
}
POST /api/security/decryptText¶
Purpose: Decrypt encrypted text (for testing)
Request:
{
"EncryptedText": "CYmF%2FyAGJuky51QOyaxql%2FwHXg8ZJXcG...",
"AuthPssword": "Password*1"
}
Response:
{
"DecryptedString": "123"
}
🔧 Helpers¶
VideoSDKHelper.cs¶
Purpose: Integration with VideoSDK for video calls
Key Methods:
GenerateToken()¶
public async Task<string> GenerateToken(string apiKey, string secretKey)
- Generate JWT token for VideoSDK API
- Claims: apiKey, permissions (allow_join, allow_mod)
- Expiration: 24 hours
CreateMeetingAsync()¶
public async Task<BaseResponse> CreateMeetingAsync(string token, string customMeetingId)
- Create video meeting room
- Optional: Enable auto-recording with transcription
- Returns meeting ID
Configuration:
- API endpoint stored in database
- API key/secret from config
- Recording enabled/disabled per organization
FCMNotificationHelper.cs¶
Purpose: Send push notifications via Firebase Cloud Messaging
Key Methods:
SendFCMMessageAsync()¶
public async Task<bool> SendFCMMessageAsync(
string topic,
string title,
string titleSLang,
string body,
string bodySLang,
object template,
bool saveToDB)
Features:
- Multi-language support (Primary + Arabic)
- Topic-based messaging
- Save to database for history
- Android/iOS platform support
Topics:
- android_doctor_{providerId}~ - Android providers
- ios_doctor_{providerId}~ - iOS providers
- android_patient_{patientId}~ - Android patients
- ios_patient_{patientId}~ - iOS patients
Notification Types:
- New booking
- Booking cancelled
- Booking reminder (30 min, 1 hour, 1 day)
- Session starting soon
SecurityHelper.cs¶
Purpose: Encryption/decryption utilities
Key Methods:
EncryptString()¶
public string EncryptString(string plainText)
- AES encryption
- Uses AESKey and AESIV from config
- URL-safe encoding
DecryptString()¶
public string DecryptString(string encryptedText)
- AES decryption
- URL-safe decoding
EncryptId() / DecryptId()¶
public string EncryptId(string id)
public long DecryptId(string encryptedId)
- Encrypt/decrypt database IDs
- Used in API requests/responses
- Prevents ID enumeration
ComputeSecureHash()¶
public string ComputeSecureHash(params string[] values)
- SHA256 hash generation
- Used for request validation
- Combines values with passphrase
XmlHelper.cs¶
Purpose: Convert objects to XML for stored procedures
Key Methods:
ObjectToXml()¶
public static string ObjectToXml<T>(T obj)
- Serialize object to XML string
- Used for complex data in stored procedures
- Handles nested objects and lists
🛡️ Action Filters¶
ValidateSecureHashAttribute¶
Purpose: Validate secure hash in request
Usage: [ValidateSecureHash]
Process:
1. Extract SecureHash from request
2. Extract request values
3. Compute expected hash
4. Compare with provided hash
5. Reject if mismatch
Example:
// Request
{
"ReferenceId": "EXT-123",
"Name": "John",
"SecureHash": "abc123..."
}
// Validation
expectedHash = SHA256(ReferenceId + Name + PassPhrase)
if (expectedHash != providedHash) {
return BadRequest("Invalid secure hash");
}
ValidateAntiXSSAttribute¶
Purpose: Prevent XSS attacks
Usage: [ValidateAntiXSS]
Process:
1. Scan all request parameters
2. Check for HTML/script tags
3. Validate against XSS patterns
4. Reject suspicious requests
Patterns Detected:
- <script>
- javascript:
- onerror=
- <iframe>
- SQL injection attempts
EncryptedModelBinderProvider¶
Purpose: Auto-decrypt encrypted IDs
Usage: Automatic via [FromBody]
Process:
1. Detects properties ending with _Decrypted
2. Finds corresponding encrypted property
3. Auto-decrypts value
4. Populates decrypted property
Example:
public class BookingRequest
{
public string UserId { get; set; } // Encrypted
public long UserId_Decrypted { get; set; } // Auto-populated
}
AuthOperationFilter¶
Purpose: Add authentication to Swagger docs
Usage: Automatic
Result: Swagger UI shows lock icon on protected endpoints
🗄️ Data Repositories¶
Total Repositories: 7¶
- AuthRepository - Authentication
- UserRepository - User management
- CareProviderRepository - Provider data
- SchedulingRepository - Schedule management
- SessionBookingRepository - Booking operations
- CommonRepository - Shared utilities
- BaseRepository - Base data access
BaseRepository¶
Purpose: Base class for all repositories
Key Methods:
- ExecuteStoredProcedure() - Execute SP
- GetConnection() - Get DB connection
- ConvertToObject<T>() - Map DataRow to object
AuthRepository¶
Stored Procedures:
- SHARED_API_AUTHENTICATE - Validate organization key
UserRepository¶
Stored Procedures:
- SHARED_API_REGISTER_ORGANIZATION_USER - Register user
- SHARED_API_VALIDATE_ORGANIZATION_USER - Validate user
- GET_SCREENING_QUESTIONS - Get assessment questions
- SAVE_USER_SCREENING - Save assessment answers
CareProviderRepository¶
Stored Procedures:
- SHARED_API_GET_CARE_PROVIDERS_LIST_FOR_GUEST - List providers (guest)
- SHARED_API_GET_CARE_PROVIDERS_LIST_FOR_LOGIN_USER - List providers (user)
- GET_CARE_PROVIDER_PROFILE - Provider details
- GET_CATALOGUE_DATA_FOR_FILTERS - Filter options
SchedulingRepository¶
Stored Procedures:
- GET_NEXT_HOURLY_SCHEDULE_FOR_CARE_PROVIDERS - Available schedules
- GET_SCHEDULE_BY_HOUR - Specific hour validation
- SAVE_SCHEDULE_BOOKING - Create booking
- UPDATE_BOOKING_STATUS - Update status
SessionBookingRepository¶
Stored Procedures:
- SHARED_API_SAVE_BOOKING_ORDER_PAY_FOR_DATA - Save booking
- SHARED_API_GET_BOOKING_DETAILS_FOR_REFUND - Get refund info
- SHARED_API_REFUND_BOOKING_PAYMENT - Process refund
- SHARED_API_GET_BOOKING_DETAILS - Get booking details
CommonRepository¶
Stored Procedures:
- APP_CONFIG_BY_GROUPID - Get app config
- GET_USER_REMINDERS_LIST - Get reminders
- SAVE_VIDEOSDK_MEETING_ID - Store meeting ID
🔄 Typical Booking Flow¶
Complete Session Booking Process¶
1. Organization Authentication:
POST /api/auth/token
Content-Type: application/x-www-form-urlencoded
access_key=ORG_KEY_123&grant_type=password
Response:
{
"access_token": "eyJhbGciOiJI...",
"token_type": "bearer",
"expires_in": 86400
}
2. Get Available Providers:
POST /api/careprovider/getcareproviderslistwithschedule
Authorization: Bearer eyJhbGciOiJI...
Content-Type: application/json
{
"ScheduleDate": "2025-11-10",
"GMTTimeDiffrenceHours": 3,
"ApplyFilter": true,
"FilterCriteria": {
"GenderType": 2,
"SpecialityId": 5
}
}
Response:
{
"Status": 1,
"Data": {
"CareProvidersList": [
{
"UserLoginInfoId": 456,
"FullName": "Dr. Sarah Ahmed",
"AvailableSlotsList": [
{
"StartTime": "10:00",
"EndTime": "11:00",
"AvailabilityId": 789
}
]
}
]
}
}
3. Book Session:
POST /api/sessionbooking/booksession
Authorization: Bearer eyJhbGciOiJI...
Content-Type: application/json
{
"ReferenceId": "EXT-12345",
"Name": "John Smith",
"DOB": "1990-05-15",
"GenderType": 1,
"CareProviderId": "encrypted_456",
"CareProviderId_Decrypted": 456,
"SlotDate": "2025-11-10",
"SlotStartTime": "10:00",
"SlotEndTime": "11:00",
"SlotAvailabilityId": 789,
"ApplicationMultiSlotId_Decrypted": 1,
"CareProviderSlotDurationId_Decrypted": 5,
"CatCommunicationTypeId": 3,
"SecureHash": "computed_hash"
}
Process:
1. Register user (if new) or validate existing
2. Validate slot availability
3. Create booking in scheduling system
4. Generate VideoSDK meeting ID
5. Update booking status to “Confirmed”
6. Send FCM notification to provider
7. Create booking reminders
Response:
{
"Status": 1,
"Reason": "Success",
"Message": "Session booked successfully",
"Data": {
"MeetingId": "abc-123-def-456",
"BookingId": "encrypted_999"
}
}
4. Provider Receives Notification:
Topic: android_doctor_456~
Title: "New Appointment Booked"
Body: "John Smith has booked an appointment with you on 2025-11-10 10:00 AM"
Data: {
"NotificationType": 1,
"SlotBookingId": 999,
"VideoSDKMeetingId": "abc-123-def-456",
...
}
5. Session Start (Provider App):
- Provider opens app
- Sees booking notification
- Joins video call using MeetingId
- Video call via VideoSDK
6. Session End (Optional Cancel):
POST /api/sessionbooking/cancelbooking
Authorization: Bearer eyJhbGciOiJI...
Content-Type: application/json
{
"BookingId": "encrypted_999",
"BookingId_Decrypted": 999,
"CareProviderId": "encrypted_456",
"CareProviderId_Decrypted": 456,
"UserId": "encrypted_123",
"UserId_Decrypted": 123,
"SecureHash": "computed_hash"
}
Process:
1. Validate booking
2. Update status to “Cancelled”
3. Refund organization credits
4. Notify provider
5. Free up slot
📊 Data Models¶
Request Models¶
AuthRequest:
public class AuthRequest
{
public string AccessKey { get; set; }
public string GrantType { get; set; }
}
UserRegistrationRequest:
public class UserRegistrationRequest
{
public string ReferenceId { get; set; }
public string Name { get; set; }
public DateTime DOB { get; set; }
public int GenderType { get; set; }
public string SecureHash { get; set; }
}
CareProviderFilterCriteria:
public class CareProviderFilterCriteria
{
public string UserId { get; set; }
public string ScheduleDate { get; set; }
public int GMTTimeDiffrenceHours { get; set; }
public bool ApplyFilter { get; set; }
public int? GenderType { get; set; }
public long? SpecialityId { get; set; }
public long? LanguageId { get; set; }
public int? MinExperience { get; set; }
public decimal? MaxFees { get; set; }
}
BookOrderRequest:
public class BookOrderRequest
{
public string UserId { get; set; }
public long UserId_Decrypted { get; set; }
public string CareProviderId { get; set; }
public long CareProviderId_Decrypted { get; set; }
public string SlotDate { get; set; }
public string SlotStartTime { get; set; }
public string SlotEndTime { get; set; }
public long SlotAvailabilityId { get; set; }
public string ApplicationMultiSlotId { get; set; }
public long ApplicationMultiSlotId_Decrypted { get; set; }
public string CareProviderSlotDurationId { get; set; }
public long CareProviderSlotDurationId_Decrypted { get; set; }
public int CatCommunicationTypeId { get; set; }
public bool IsBookingFromMobile { get; set; }
public int BookingPlatformId { get; set; }
public string ReferenceId { get; set; }
public string Name { get; set; }
public DateTime? DOB { get; set; }
public int? GenderType { get; set; }
public string SecureHash { get; set; }
}
Response Models¶
BaseResponse:
public class BaseResponse
{
public int Status { get; set; }
public ResponseReason Reason { get; set; }
public string Message { get; set; }
public object Data { get; set; }
}
CareProvidersListResponse:
public class CareProvidersListResponse
{
public List<CareProviderInfo> CareProvidersList { get; set; }
}
public class CareProviderInfo
{
public long UserLoginInfoId { get; set; }
public string FullName { get; set; }
public string FirstNamePLang { get; set; }
public string LastNamePLang { get; set; }
public string FirstNameSLang { get; set; }
public string LastNameSLang { get; set; }
public int GenderType { get; set; }
public string Speciality { get; set; }
public int Experience { get; set; }
public decimal ConsultationFee { get; set; }
public string ProfileImage { get; set; }
public decimal Rating { get; set; }
public int TotalRatings { get; set; }
public List<string> Languages { get; set; }
public List<AvailableHour> AvailableScheduleHoursList { get; set; }
public List<AvailableSlot> AvailableSlotsList { get; set; }
public SlotDuration SlotDurationType { get; set; }
}
🔒 Security Features¶
Multi-Layer Security¶
1. API Key Authentication:
- Organization-level access key
- Validated against database
- Must be active organization
2. JWT Token:
- Bearer token authentication
- 24-hour expiration
- Claims-based authorization
- Organization ID embedded
3. Secure Hash Validation:
- SHA256 hash of request parameters
- Prevents parameter tampering
- Validates request integrity
4. ID Encryption:
- All entity IDs encrypted
- Prevents ID enumeration
- AES encryption
5. Anti-XSS Protection:
- Validates all input parameters
- Rejects HTML/script tags
- Prevents code injection
6. Organization Isolation:
- Users belong to organizations
- Can only access own users
- No cross-organization access
🚀 Deployment¶
Azure DevOps Pipeline¶
File: azure-pipelines.yml
Pipeline Steps:
-
Stop Application Pool:
Stop-WebAppPool -Name "psyter_tuhoon" -
Restore NuGet Packages:
nuget restore **/*.sln -
Install .NET 8 SDK:
UseDotNet@2: version: '8.0.x' -
Build Project:
dotnet build --configuration Release -
Publish:
dotnet publish --configuration Release --output $(Build.ArtifactStagingDirectory) -
Create Artifact:
- Zip published files
- Store in artifact directory -
Copy to Deployment:
Source: $(Build.ArtifactStagingDirectory) Target: D:\ROOT\Development\Psyter\Master\agent\ -
Extract:
Extract: D:\ROOT\Development\Psyter\Master\agent\*.zip Destination: D:\ROOT\Development\Psyter\Master\TuhoonAPIs -
Start Application Pool:
Start-WebAppPool -Name "psyter_tuhoon"
Deployment Target:
D:\ROOT\Development\Psyter\Master\TuhoonAPIs
IIS Configuration:
- App Pool: psyter_tuhoon
- .NET Version: .NET 8.0
- Pipeline Mode: Integrated
- URL: https://dvx.innotech-sa.com/Psyter/Master/TuhoonAPIs
📚 API Documentation (Swagger)¶
Access Swagger UI¶
URL: https://dvx.innotech-sa.com/Psyter/Master/TuhoonAPIs/swagger
Features:
- Interactive API testing
- Request/response examples
- Try-it-out functionality
- Authentication testing
- Schema documentation
Swagger Configuration¶
Program.cs:
builder.Services.AddSwaggerGen(options =>
{
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "Enter JWT: Bearer eyJhbGciOiJI..."
});
options.OperationFilter<AuthOperationFilter>();
});
Swagger Features:
- Automatic endpoint discovery
- JWT authentication UI
- Request validation
- Response examples
- Error documentation
🔄 Integration Patterns¶
Pattern 1: Guest Booking (No Pre-registration)¶
Use Case: External organization books session for new user
1. Get Token (/api/auth/token)
2. Get Providers (/api/careprovider/getcareproviderslistwithschedule)
3. Book Session (/api/sessionbooking/booksession)
- Provide ReferenceId, Name, DOB
- System auto-registers user
- Returns BookingId + MeetingId
Pattern 2: Registered User Booking¶
Use Case: External organization books for existing user
1. Get Token
2. Register User (/api/user/register)
- Store UserId
3. Get Providers
4. Book Session
- Use stored UserId
- No need for Name/DOB
Pattern 3: Provider Search with Filters¶
Use Case: Find specific type of provider
1. Get Token
2. Get Filter Options (/api/careprovider/getcataloguedataforfilters)
3. Get Providers with Filters
- Apply GenderType, Speciality, Language, etc.
- Get only available slots for date
📊 Differences from Main API¶
Technology¶
| Feature | Main API | Tahoon API |
|---|---|---|
| Framework | .NET Framework 4.7.2 | .NET 8 |
| API Style | Web API 2 | ASP.NET Core |
| Auth | OAuth 2.0 | JWT Bearer |
| DI | Autofac | Built-in |
| Swagger | Swashbuckle 5 | Swashbuckle 6 |
Functionality¶
| Feature | Main API | Tahoon API |
|---|---|---|
| Users | All users | Organization users only |
| Registration | Full profile | Minimal (ReferenceId/Name/DOB) |
| Payment | Full payment flow | No payment (charity credits) |
| Booking | All booking types | Session booking only |
| Profile | Full management | Read-only |
| Messaging | Full chat | Not included |
| Notifications | Via NodeServer | Direct FCM |
Security¶
| Feature | Main API | Tahoon API |
|---|---|---|
| Auth | User credentials | Organization API key |
| Scope | User-level | Organization-level |
| Validation | Standard | Secure hash required |
| IDs | Plain | Encrypted |
| XSS | Manual checks | Automatic filter |
🎯 Key Benefits¶
For External Organizations¶
✅ Easy Integration: RESTful API with clear documentation
✅ Secure Access: Organization-level authentication
✅ Auto User Registration: No pre-registration needed
✅ Zero Payment Hassle: Uses organization credits
✅ Real-time Availability: Check provider schedules instantly
✅ Video Calls Included: VideoSDK integration built-in
✅ Push Notifications: Automatic FCM notifications
✅ Swagger Documentation: Interactive API testing
For Psyter Platform¶
✅ Revenue: Partner organizations pay for credits
✅ Scale: Support multiple organizations
✅ Isolation: Organization data separation
✅ Flexibility: Different rules per organization
✅ Tracking: Reference IDs link to external systems
✅ Modern Tech: .NET 8 performance benefits
📈 Performance Considerations¶
Optimization Strategies¶
1. Connection Pooling:
- SQL Server connection pool
- Reuse database connections
- Configurable timeout
2. Async/Await:
- All repository methods async
- Non-blocking I/O operations
- Better scalability
3. Caching:
- Cache filter options (specialties, languages)
- Cache organization data
- Reduce database queries
4. Minimal Data:
- Only return necessary fields
- Pagination support ready
- Compressed responses
🔧 Maintenance¶
Configuration Updates¶
Update JWT Key:
"Jwt": {
"Key": "NEW_KEY_HERE"
}
Update Organization Access:
UPDATE Organizations
SET SharedAPIKey = 'NEW_KEY',
IsActive = 1
WHERE OrganizationId = 123
Monitoring¶
Health Checks:
- Database connectivity
- VideoSDK API availability
- FCM service status
- Response times
Logs:
- ASP.NET Core logging
- IIS logs
- Application Insights (if configured)
END OF TAHOON API DOCUMENTATION
Completion Status:
- ✅ Android Client (COMPLETED)
- ✅ AndroidCareProvider (COMPLETED)
- ✅ APIs - Part 1 (COMPLETED)
- ✅ APIs - Part 2 (COMPLETED)
- ✅ Media API (COMPLETED)
- ✅ NodeServer (COMPLETED)
- ✅ Tahoon_API (COMPLETED)
- ⏭️ Web (NEXT)
- Pending: WindowsService, IOSCareProvider