# 🌐 N8n Webhook Integration Guide ## Endpoint Configuration ### Production Endpoint ``` URL: https://n8n.lab.ubharajaya.ac.id/webhook/23c6993d-1792-48fb-ad1c-ffc78a3e6254 Method: POST Content-Type: application/json ``` ### Testing Endpoint ``` URL: https://n8n.lab.ubharajaya.ac.id/webhook-test/23c6993d-1792-48fb-ad1c-ffc78a3e6254 Method: POST Content-Type: application/json ``` ### Switch Configuration **File**: `config/AppConfig.kt` ```kotlin // Production (default) const val USE_WEBHOOK = N8N_WEBHOOK_PROD // Switch ke testing const val USE_WEBHOOK = N8N_WEBHOOK_TEST ``` --- ## Request Format ### JSON Payload Example ```json { "npm": "12345678", "nama": "John Doe", "latitude": -6.896123, "longitude": 107.610056, "timestamp": 1704067200000, "foto_base64": "iVBORw0KGgoAAAANSUhEUgAAA...ABJRU5ErkJggg==" } ``` ### Field Descriptions | Field | Type | Example | Notes | |-------|------|---------|-------| | `npm` | String | "12345678" | Nomor Pokok Mahasiswa | | `nama` | String | "John Doe" | Nama lengkap mahasiswa | | `latitude` | Number | -6.896123 | Koordinat latitude (format: 6 desimal) | | `longitude` | Number | 107.610056 | Koordinat longitude (format: 6 desimal) | | `timestamp` | Number | 1704067200000 | Unix timestamp dalam milliseconds | | `foto_base64` | String | "iVBORw0K..." | Foto compressed JPEG dalam Base64 | --- ## Response Handling ### Success Response (HTTP 200/201) ```json { "success": true, "message": "Attendance recorded successfully", "data": { "id": "65a7b8c9d0e1f2g3h4i5j6k7", "npm": "12345678", "nama": "John Doe", "timestamp": 1704067200000, "status": "accepted" } } ``` **App Behavior**: - Status badge: ✅ ACCEPTED (green) - Message: "✓ Absensi berhasil dikirim!" - Database: Save with status = "accepted" ### Error Response (HTTP 400+) ```json { "error": true, "message": "Location outside of allowed area", "code": "LOCATION_INVALID" } ``` **App Behavior**: - Status badge: ✗ REJECTED (red) - Message: "✗ Gagal: Server returned code: 400" - Database: Save with status = "rejected" ### Server Timeout (no response) **App Behavior**: - After 10 seconds: Show error - Message: "✗ Gagal: timeout" - Database: Save with status = "rejected" --- ## Real-World Examples ### Example 1: Valid Attendance **Request**: ```bash curl -X POST https://n8n.lab.ubharajaya.ac.id/webhook/23c6993d-1792-48fb-ad1c-ffc78a3e6254 \ -H "Content-Type: application/json" \ -d '{ "npm": "12345678", "nama": "Arif Rachman Dwi", "latitude": -6.8961, "longitude": 107.6100, "timestamp": 1704067200000, "foto_base64": "[COMPRESSED_JPEG_BASE64]" }' ``` **Response** (Success): ```json HTTP/1.1 200 OK { "success": true, "message": "Attendance recorded", "status": "accepted" } ``` --- ### Example 2: Location Out of Area **Request**: ```bash curl -X POST https://n8n.lab.ubharajaya.ac.id/webhook/23c6993d-1792-48fb-ad1c-ffc78a3e6254 \ -H "Content-Type: application/json" \ -d '{ "npm": "12345678", "nama": "Arif Rachman Dwi", "latitude": -6.8, "longitude": 107.5, "timestamp": 1704067200000, "foto_base64": "[COMPRESSED_JPEG_BASE64]" }' ``` **Response** (Out of Radius): ```json HTTP/1.1 400 Bad Request { "error": true, "message": "Location is outside of allowed area", "code": "LOCATION_INVALID", "distance": 12500 } ``` --- ## Image Compression Details ### Photo Processing in App 1. **Capture**: Camera Intent mengambil JPEG thumbnail 2. **Compress**: Bitmap compressed dengan quality 80% 3. **Encode**: JPEG bytes di-encode ke Base64 4. **Size**: Typical size ~50-100 KB (Base64 ~70-130 KB) ### Configuration **File**: `config/AppConfig.kt` ```kotlin const val PHOTO_COMPRESS_QUALITY = 80 // Range: 0-100 ``` ### Compression Quality Reference | Quality | Use Case | |---------|----------| | 50 | Minimal quality, smallest size | | 80 | Balanced (DEFAULT) | | 100 | Maximum quality, largest size | --- ## N8n Workflow Integration ### Expected Workflow Steps 1. **Webhook Trigger** - Menerima POST request dengan JSON payload - Validasi format & required fields 2. **Data Validation** - Check NPM format - Validate coordinates (lat/lon ranges) - Validate timestamp (not in future) - Check foto_base64 format 3. **Location Validation** - Calculate distance from campus center - Check if within allowed radius - Return error if out of bounds 4. **Photo Processing** - Decode Base64 image - Optional: Save to storage - Optional: Run face detection - Optional: Save metadata 5. **Database Storage** - Insert into attendance table - Record timestamp & location - Update student statistics 6. **Response** - Return HTTP 200 + success message - Or HTTP 400 + error message ### Sample N8n Workflow JSON See `n8n-workflow-EAS.json` in project root for complete workflow configuration. --- ## Monitoring & Testing ### Monitoring URL ``` https://ntfy.ubharajaya.ac.id/EAS ``` Monitor real-time webhook notifications: - New attendance submissions - Errors & rejections - System alerts ### Spreadsheet Tracking ``` https://docs.google.com/spreadsheets/d/1jH15MfnNgpPGuGeid0hYfY7fFUHCEFbCmg8afTyyLZs/ ``` Track all recorded attendance data: - Student information - Location & timestamp - Photo references - Status & notes ### Testing Endpoint ``` https://n8n.lab.ubharajaya.ac.id/webhook-test/23c6993d-1792-48fb-ad1c-ffc78a3e6254 ``` Use for testing before production: - Simulates webhook behavior - Doesn't actually save to production database - Useful for app development & testing --- ## Implementation Code Reference ### Sending Request (AttendanceRepository.kt) ```kotlin fun sendToN8n( onSuccess: () -> Unit, onError: (String) -> Unit, attendance: Attendance, foto: Bitmap ) { thread { try { val url = URL(AppConfig.USE_WEBHOOK) val conn = url.openConnection() as HttpURLConnection conn.requestMethod = "POST" conn.setRequestProperty("Content-Type", "application/json") conn.doOutput = true conn.connectTimeout = 10000 conn.readTimeout = 10000 // Create JSON payload val json = JSONObject().apply { put("npm", attendance.npm) put("nama", attendance.nama) put("latitude", attendance.latitude) put("longitude", attendance.longitude) put("timestamp", attendance.timestamp) put("foto_base64", bitmapToBase64(foto)) } // Send request conn.outputStream.use { it.write(json.toString().toByteArray()) it.flush() } // Handle response val responseCode = conn.responseCode if (responseCode == 200 || responseCode == 201) { onSuccess() } else { onError("Server returned code: $responseCode") } conn.disconnect() } catch (e: Exception) { onError(e.message ?: "Unknown error") } } } ``` --- ## Troubleshooting ### 1. Connection Timeout ``` Error: "✗ Gagal: timeout" Solution: - Check internet connection - Verify URL is correct - Test network connectivity - Increase timeout if needed ``` ### 2. Invalid JSON Response ``` Error: "✗ Gagal: JSON parsing error" Solution: - Verify payload format - Check all required fields present - Validate Base64 string - Check JSON syntax ``` ### 3. 400 Bad Request ``` Error: "✗ Gagal: Server returned code: 400" Possible causes: - Invalid NPM format - Coordinates out of valid range - Missing required fields - Malformed Base64 image ``` ### 4. 401 Unauthorized ``` Error: "✗ Gagal: Server returned code: 401" Solution: - Check webhook URL is correct - Verify webhook is active in N8n - Check authentication headers if required ``` ### 5. 403 Forbidden ``` Error: "✗ Gagal: Server returned code: 403" Solution: - Verify webhook permissions - Check if webhook is enabled - Verify IP whitelist settings ``` ### 6. 500 Server Error ``` Error: "✗ Gagal: Server returned code: 500" Solution: - Check N8n workflow logs - Verify database connection - Check N8n service status - Contact system administrator ``` --- ## Security Considerations ### Data Protection - All data sent over HTTPS - No sensitive data stored in logs - Base64 photo cannot be directly viewed - Timestamp validation prevents replay attacks ### Rate Limiting - Consider implementing rate limiting on N8n - Prevent duplicate submissions - Monitor for unusual activity ### Validation - Server should validate all inputs - Reject invalid coordinates - Verify photo file integrity - Check timestamp within reasonable window --- ## API Contract ### Version Information ``` API Version: v1 Last Updated: 2025-01-14 Endpoint Status: Active ``` ### Backward Compatibility - Current version: 1.0 - No breaking changes planned - All fields are required - Response format is stable ### Future Enhancements - [ ] Support for batch submissions - [ ] Webhook signature verification - [ ] Custom error codes - [ ] Rate limiting headers - [ ] API versioning in URL --- **Document Version**: 1.0 **Last Updated**: 2025-01-14 **Status**: ✅ Complete & Current