Psyter Shared API (Tahoon API)¶
Overview¶
The Psyter Shared API (also known as Tahoon API) is a .NET 8.0 Web API that serves as a shared integration layer for external organizations partnering with the Psyter platform. This API enables third-party healthcare organizations to integrate with Psyter’s telemedicine services, allowing them to:
- Register their patients/users within the Psyter ecosystem
- Search and filter care providers based on various criteria
- View provider availability and schedules
- Book therapy sessions on behalf of their users
- Manage session bookings (view, cancel)
Unlike the main Psyter API (which serves the core platform), the Tahoon API is specifically designed for B2B integrations where external organizations can offer Psyter’s mental health services to their own user base while maintaining their own UI/UX.
Key Characteristics:
- Authentication: OAuth 2.0 with JWT bearer tokens
- Multi-tenant: Supports multiple organizations with isolated data
- Security-focused: Hash validation, XSS protection, encrypted IDs
- Integration-ready: RESTful API with Swagger documentation
Architecture¶
Technology Stack¶
| Component | Technology | Version |
|---|---|---|
| Framework | .NET / ASP.NET Core | 8.0 |
| Language | C# | Latest |
| Authentication | JWT Bearer Tokens | 8.0.18 |
| API Documentation | Swagger / Swashbuckle | 6.6.2 |
| Database Access | ADO.NET (SqlClient) | 4.9.0 |
| External Services | Firebase Cloud Messaging | v1 |
| Serialization | Newtonsoft.Json | 13.0.3 |
| Video SDK | VideoSDK.live | - |
Architecture Pattern¶
Repository Pattern with Service Layer
Controllers
↓
Repositories (IRepository interfaces)
↓
BaseRepository (Database access)
↓
SQL Server (PsyterDatabase, SchedulingDatabase)
Helper Services:
- SecurityHelper - Encryption, hashing, ID obfuscation
- VideoSDKHelper - Video meeting room creation
- FCMNotificationHelper - Push notifications
- XmlHelper - XML serialization for legacy stored procedures
Project Structure¶
PsyterSharedAPI/
├── Controllers/ # API endpoint controllers
│ ├── AuthController.cs # Token generation
│ ├── UserController.cs # User registration & assessment
│ ├── CareProviderController.cs # Provider listing & schedules
│ ├── SessionBookingController.cs # Booking & cancellation
│ └── SecurityController.cs # Security utilities
├── Data/
│ ├── Model/ # Request/Response DTOs
│ │ ├── AuthRequest.cs
│ │ ├── UserRequest.cs
│ │ ├── CareProviderRequest.cs
│ │ ├── SessionBooking.cs
│ │ ├── Enumeration.cs
│ │ └── ...
│ └── Repositories/ # Data access layer
│ ├── Interfaces/
│ ├── BaseRepository.cs
│ ├── AuthRepository.cs
│ ├── UserRepository.cs
│ ├── CareProviderRepository.cs
│ ├── SchedulingRepository.cs
│ ├── SessionBookingRepository.cs
│ └── CommonRepository.cs
├── Helpers/ # Business logic helpers
│ ├── SecurityHelper.cs # Encryption/decryption
│ ├── VideoSDKHelper.cs # Video SDK integration
│ ├── FCMNotificationHelper.cs # Push notifications
│ └── XmlHelper.cs # XML utilities
├── ActionFilters/ # Request validation
│ ├── ValidateSecureHashFilter.cs # Hash validation
│ ├── AntiXssValidationFilter.cs # XSS prevention
│ ├── ModelBinderToDecryptValue.cs # ID decryption
│ └── SwaggerActionFilter.cs # Swagger auth filter
├── Extensions/
│ └── DataRowExtensions.cs # DataRow to object mapping
├── appsettings.json # Configuration
├── Program.cs # Application startup
└── PsyterSharedAPI.csproj # Project file
Key Features¶
1. Multi-Tenant Authentication¶
- Organizations receive a unique
ApplicationToken(API Key) - OAuth 2.0 token endpoint (
/api/auth/token) - JWT tokens valid for 24 hours
- Token contains
OrganizationIdandSharedAPIKeyclaims
2. User Management¶
- User Registration: Register organization users with encrypted data
- Assessment Questionnaires: Mental health screening questions
- User Validation: Ensure users belong to requesting organization
3. Care Provider Discovery¶
- Advanced Filtering: By gender, language, specialty, experience, etc.
- Real-time Availability: Schedule integration with hourly/slot-based availability
- Profile Data: Provider credentials, bio, ratings, languages
- Catalogue Data: Filter options (specialties, languages, experience levels)
4. Session Booking¶
- Book Sessions: Create appointments with automatic slot validation
- Cancel Bookings: Cancellation with refund handling
- Video Meeting Creation: Auto-generate VideoSDK meeting rooms
- Notifications: FCM push notifications to providers
- Charity Organization Support: Free/subsidized sessions for partner orgs
5. Security Features¶
- Encrypted IDs: All sensitive IDs encrypted in transit
- Hash Validation: Request integrity verification using HMAC-SHA256
- Anti-XSS: Input validation against XSS attacks
- Encrypted Connection Strings: Database credentials encrypted in config
- Custom Model Binding: Automatic ID decryption
Getting Started¶
Prerequisites¶
- .NET 8.0 SDK or later
- SQL Server (two databases:
PsyterDatabase,SchedulingDatabase) - Firebase project (for push notifications)
- VideoSDK account (for video meetings)
- IIS or Azure App Service (for deployment)
Installation¶
-
Clone the repository:
git clone <repository-url> cd Tahoon_API/PsyterSharedAPI -
Restore NuGet packages:
dotnet restore -
Configure appsettings.json:
Update the following sections:
{
"ConnectionStrings": {
"PsyterDatabase": "<encrypted-connection-string>",
"SchedulingDatabase": "<encrypted-connection-string>"
},
"Jwt": {
"Key": "your-secret-key",
"Issuer": "psyter.com",
"Audience": "psyter_client"
},
"SecuritySettings": {
"AESKey": "<base64-aes-key>",
"AESIV": "<base64-aes-iv>",
"PassPhrase": "<encryption-passphrase>",
"Salt": "<salt-value>",
"Vector": "<vector-value>"
}
}
- Add Firebase credentials:
Place firebase-adminsdk-live.json in the project root.
-
Build the project:
dotnet build --configuration Release -
Run the application:
dotnet run
Or in production:
dotnet publish -c Release
Database Setup¶
The API requires two SQL Server databases:
-
PsyterDatabase - Main application database
- User accounts and profiles
- Care provider information
- Booking orders and payments
- Organization data -
SchedulingDatabase - Scheduling system
- Provider availability
- Hourly schedules
- Slot bookings
- Booking status tracking
Note: Stored procedures are required. Contact the development team for database schema and stored procedure scripts.
Configuration¶
Connection Strings¶
Connection strings are encrypted in appsettings.json. To encrypt a connection string:
- Use the
SecurityHelper.EncryptString()method - Encrypt these components separately:
-Data Source
-Initial Catalog
-User Id
-Password
Example plaintext format before encryption:
Data Source=server.database.windows.net;Initial Catalog=PsyterDB;User Id=admin;Password=pass123;
Security Settings¶
{
"SecuritySettings": {
"AESKey": "base64-encoded-32-byte-key",
"AESIV": "base64-encoded-16-byte-iv",
"PassPhrase": "UUID-style-passphrase",
"Salt": "16-character-salt",
"Vector": "16-character-vector",
"EncryptionPassword": "Password*1"
}
}
JWT Configuration¶
{
"Jwt": {
"Key": "unique-secret-key-min-32-chars",
"Issuer": "psyter.com",
"Audience": "psyter_client"
}
}
API Endpoints¶
Authentication¶
POST /api/auth/token¶
Get an OAuth 2.0 bearer token for API access.
Request (form-data):
grant_type=password
access_key=<organization-api-key>
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 86400
}
User Management¶
POST /api/user/register¶
Register a new user under the organization.
Headers:
- Authorization: Bearer <token>
Request:
{
"referenceId": "ORG-USER-12345",
"name": "John Doe",
"dob": "1990-05-15",
"genderType": 1,
"secureHash": "<hmac-sha256-hash>"
}
Response:
{
"status": 1,
"message": "Success",
"reason": 2,
"data": {
"userLoginInfoId": "<encrypted-user-id>"
}
}
GET /api/user/getassessmentquestions¶
Retrieve mental health screening questions.
Response:
{
"status": 1,
"data": {
"screeningQuestions": [...]
}
}
POST /api/user/submituserassessmentquestions¶
Submit user assessment answers.
Care Provider Discovery¶
POST /api/careprovider/getcareproviderslistwithschedule¶
Search care providers with optional filtering and availability.
Request:
{
"userId": "<encrypted-user-id>",
"scheduleDate": "2025-11-15",
"gmtTimeDiffrenceHours": 3,
"applyFilter": true,
"filterCriteriaObject": {
"genderType": 0,
"languageIds": [1, 2],
"specialtyIds": [3],
"experienceLevel": 2
}
}
Response:
{
"status": 1,
"data": {
"careProvidersList": [
{
"userLoginInfoId": 123,
"fullName": "Dr. Sarah Ahmed",
"genderType": 0,
"availableScheduleHoursList": [...],
"availableSlotsList": [...]
}
]
}
}
POST /api/careprovider/getcareproviderschedule¶
Get detailed schedule for a specific provider.
POST /api/careprovider/getcareprovidersprofiledata¶
Get full profile information for a care provider.
GET /api/careprovider/getcataloguedataforfilters¶
Get filter options (specialties, languages, etc.).
Session Booking¶
POST /api/sessionbooking/booksession¶
Book a therapy session.
Request:
{
"userId": "<encrypted-user-id>",
"careProviderId": "<encrypted-provider-id>",
"slotDate": "2025-11-15",
"slotStartTime": "10:00:00",
"slotEndTime": "11:00:00",
"applicationMultiSlotId": "<encrypted-service-id>",
"catCommunicationTypeId": 1,
"isBookingFromMobile": false,
"bookingPlatformId": 3,
"secureHash": "<hmac-sha256-hash>"
}
Response:
{
"status": 1,
"message": "Success",
"data": {
"meetingId": "abcd-1234-efgh-5678",
"bookingId": "<encrypted-booking-id>"
}
}
POST /api/sessionbooking/cancelbooking¶
Cancel a booked session.
Request:
{
"bookingId": "<encrypted-booking-id>",
"userId": "<encrypted-user-id>",
"careProviderId": "<encrypted-provider-id>",
"secureHash": "<hmac-sha256-hash>"
}
Security Implementation¶
Secure Hash Validation¶
For endpoints marked with [ValidateSecureHash], requests must include a secureHash field.
Hash Calculation:
1. Concatenate all properties marked with [IncludeInHash] attribute
2. Sort alphabetically by property name
3. Generate HMAC-SHA256 hash using SharedAPIKey from JWT token
Example (C# client):
public string GenerateSecureHash(object request, string sharedApiKey)
{
var hashValues = GetIncludedHashValues(request);
var data = string.Join("", hashValues.OrderBy(kv => kv.Key).Select(kv => kv.Value));
using var hmac = new HMACSHA256(Convert.FromBase64String(sharedApiKey));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
return Convert.ToBase64String(hash);
}
ID Encryption¶
All sensitive IDs (user, provider, booking) are encrypted using AES-256:
Format: Base64URL(AES256-CBC(ID))
Decryption (automatic via model binding):
- Properties marked with [Encrypted] attribute
- Automatically decrypted to {PropertyName}_Decrypted field
Deployment¶
Azure DevOps Pipeline¶
The project includes an azure-pipelines.yml for CI/CD:
- Trigger: Push to
masterbranch - Build: .NET 8.0 build
- Publish: Creates deployment package
- Deploy: Extracts to IIS application pool
psyter_tuhoon
Deployment Path: D:\ROOT\Development\Psyter\Master\TuhoonAPIs
IIS Configuration¶
- Create Application Pool:
psyter_tuhoon - Set .NET CLR Version:
No Managed Code - Set Application Path:
/Psyter/Master/TuhoonAPIs - Configure HTTPS bindings
Environment Configuration¶
- Development: Uses
appsettings.Development.json - Production: Uses
appsettings.json
AppBasePath setting controls Swagger endpoint URL in production.
Error Handling¶
Response Format¶
All API responses follow this structure:
{
"status": 0, // 0 = error, 1 = success
"message": "Description",
"reason": 18, // Enumeration.ResponseReason value
"data": null
}
Common Error Codes¶
| Reason | Code | Description |
|---|---|---|
| EmptyParameter | 1 | Missing required fields |
| UserNotFound | 18 | Invalid user ID |
| NotAllowed | 16 | Action not permitted |
| RecordAlreadyExist | 4 | Duplicate booking |
| CharityOrganizationBalanceOut | 50 | Insufficient balance |
Testing¶
Swagger UI¶
Available at: https://<base-url>/swagger
Note: Swagger is enabled in both Development and Production (for integration testing).
Authentication in Swagger¶
- Click “Authorize” button
- Enter token:
Bearer <your-jwt-token> - All protected endpoints will include the token
Sample Workflow¶
-
Get Token:
POST /api/auth/token grant_type=password&access_key=your-api-key -
Register User:
POST /api/user/register Authorization: Bearer <token> -
Search Providers:
POST /api/careprovider/getcareproviderslistwithschedule -
Book Session:
POST /api/sessionbooking/booksession
Integration Guide¶
Client Implementation Checklist¶
- Obtain
ApplicationTokenfrom Psyter team - Implement token refresh (24-hour expiry)
- Implement SecureHash generation
- Handle encrypted ID formats
- Store user mappings (
ReferenceId↔ PsyterUserLoginInfoId) - Implement error handling for all response reasons
- Test booking workflow end-to-end
- Implement FCM notification handling (optional)
Best Practices¶
-
Token Management:
- Cache JWT tokens (valid 24 hours)
- Implement automatic refresh before expiry -
ID Handling:
- Store encrypted IDs returned from API
- Never decrypt IDs client-side (treat as opaque) -
Error Handling:
- Checkstatusfield (0/1) in all responses
- Usereasonenum for specific error handling
- Display user-friendly messages frommessagefield -
User Registration:
- Use uniquereferenceIdfor your users
- Store mapping between your user ID and Psyter encrypted ID -
Booking Flow:
- Always validate slot availability before booking
- HandleRecordAlreadyExist(slot taken) gracefully
- StoremeetingIdfor video session access
Monitoring & Logging¶
Application Insights¶
(To be configured)
Log Locations¶
- Development: Console output
- Production: Windows Event Log / File system
Key Metrics to Monitor¶
- Token generation success rate
- Booking success/failure ratio
- API response times
- Database connection pool health
- VideoSDK meeting creation success rate
Troubleshooting¶
Common Issues¶
1. 401 Unauthorized¶
- Cause: Invalid or expired JWT token
- Solution: Regenerate token using
/api/auth/token
2. SecureHash Validation Failed¶
- Cause: Incorrect hash calculation
- Solution: Verify
SharedAPIKeyin JWT claims, ensure correct property ordering
3. Slot Not Available¶
- Cause: Another user booked the slot
- Solution: Refresh schedule and select another time
4. CharityOrganizationBalanceOut¶
- Cause: Organization quota exhausted
- Solution: Contact Psyter to increase quota
5. Connection String Decryption Error¶
- Cause: Invalid encryption keys in
SecuritySettings - Solution: Verify
PassPhrase,Salt,Vectormatch encryption values
Dependencies¶
NuGet Packages¶
<PackageReference Include="Google.Apis.FirebaseCloudMessaging.v1" Version="1.70.0.3813" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.18" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.13.0" />
External Services¶
- Firebase Cloud Messaging: Push notifications
- VideoSDK.live: Video conferencing infrastructure
- SQL Server: Data persistence
Related Documentation¶
Support¶
For integration support:
- Email: dev@psyter.com
- Documentation: https://docs.psyter.com
- Issue Tracking: Internal JIRA
Changelog¶
Version 1.0.0 (Current)¶
- .NET 8.0 migration
- JWT authentication
- OAuth 2.0 token endpoint
- Multi-tenant support
- VideoSDK integration
- FCM notifications
- Encrypted ID support
- SecureHash validation
License¶
Proprietary - Psyter Platform