9.2 KiB
9.2 KiB
🌐 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
// Production (default)
const val USE_WEBHOOK = N8N_WEBHOOK_PROD
// Switch ke testing
const val USE_WEBHOOK = N8N_WEBHOOK_TEST
Request Format
JSON Payload Example
{
"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)
{
"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+)
{
"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:
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):
HTTP/1.1 200 OK
{
"success": true,
"message": "Attendance recorded",
"status": "accepted"
}
Example 2: Location Out of Area
Request:
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):
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
- Capture: Camera Intent mengambil JPEG thumbnail
- Compress: Bitmap compressed dengan quality 80%
- Encode: JPEG bytes di-encode ke Base64
- Size: Typical size ~50-100 KB (Base64 ~70-130 KB)
Configuration
File: config/AppConfig.kt
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
-
Webhook Trigger
- Menerima POST request dengan JSON payload
- Validasi format & required fields
-
Data Validation
- Check NPM format
- Validate coordinates (lat/lon ranges)
- Validate timestamp (not in future)
- Check foto_base64 format
-
Location Validation
- Calculate distance from campus center
- Check if within allowed radius
- Return error if out of bounds
-
Photo Processing
- Decode Base64 image
- Optional: Save to storage
- Optional: Run face detection
- Optional: Save metadata
-
Database Storage
- Insert into attendance table
- Record timestamp & location
- Update student statistics
-
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)
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