Uploads business documents required for merchant onboarding and KYC (Know Your Customer) verification. This endpoint accepts multiple document types including valid IDs, business permits, certificates, and supporting documents.
Binary File Upload Required: This endpoint expects actual binary file content via multipart/form-data. Do NOT send base64-encoded strings, file paths, or URLs. Upload the actual file data using proper multipart form encoding.
Rate Limiting: Maximum 5 requests per 60 seconds to prevent abuse.
Security: This endpoint implements OWASP File Upload Cheat Sheet best practices including file extension validation, MIME type validation, content validation using magic bytes, file size limits, and secure storage in S3 with UUID naming.
Content-Type: This endpoint requires multipart/form-data encoding. Each field should contain the actual binary file content, not strings, URLs, or base64-encoded data.
This endpoint accepts binary file uploads via multipart/form-data. When uploading files, ensure you’re using proper file upload mechanisms in your programming language (e.g., fs.createReadStream() in Node.js, open() in Python, CURLFile in PHP) rather than sending text or encoded strings.
Valid government-issued IDs of authorized signatories (front side)Required for: All business typesType: Binary file upload (not string)Accepted formats: JPEG, JPG, PNG, PDFMax size: 25MB per fileExamples: Driver’s License, Passport, National ID, SSS ID, TIN ID
Upload the actual image/PDF file, not a file path, URL, or base64 string
Photo of the store/business establishmentRequired for: STARTER business typeRequirements: Must show visible store name/signageType: Binary file uploadAccepted formats: JPEG, JPG, PNGMax size: 25MB per file
Cause: The merchantId in the URL is not a valid numeric valueSolution: Ensure merchantId contains only digits
400 Bad Request - Merchant ID Mismatch
Status Code:400
Copy
Ask AI
{ "statusCode": 400, "message": "Merchant ID mismatch", "error": "Bad Request"}
Cause: Regular user attempting to upload files for a different merchant accountSolution: Use the correct merchantId that matches your authenticated account, or use Super Admin credentials
400 Bad Request - No Files Received
Status Code:400
Copy
Ask AI
{ "statusCode": 400, "message": "No files received", "error": "Bad Request"}
Cause: Request didn’t include any filesSolution: Include at least one file in the multipart/form-data request
Cause: File size exceeds 25MB limitSolution: Reduce file size or split into multiple files
400 Bad Request - Failed to Save
Status Code:400
Copy
Ask AI
{ "statusCode": 400, "message": "Failed to save merchant's VALID_IDS_OF_SIGNATORY file/s", "error": "Bad Request"}
Cause: Error saving files to S3 or databaseSolution: Retry the request. If issue persists, contact support
401 Unauthorized
Status Code:401
Copy
Ask AI
{ "statusCode": 401, "message": "Account not found", "error": "Unauthorized"}
Cause: Invalid or missing JWT Bearer tokenSolution: Ensure valid authentication token is provided in Authorization header
429 Too Many Requests
Status Code:429
Copy
Ask AI
{ "statusCode": 429, "message": "ThrottlerException: Too Many Requests"}
Cause: Rate limit exceeded (more than 5 requests in 60 seconds)Solution: Wait 60 seconds before retrying. Implement exponential backoff in your application
Error:No files received or Invalid file content typeIssue: Sending file paths, URLs, or base64-encoded strings instead of actual binary file dataIncorrect Examples:
// CORRECT - Node.js with file streamconst fs = require('fs');formData.append('idsOfValidSignatories', fs.createReadStream('/path/to/file.jpg'));// CORRECT - Browser with File objectconst fileInput = document.getElementById('fileInput');formData.append('idsOfValidSignatories', fileInput.files[0]);// CORRECT - Python with file handlefiles = {'idsOfValidSignatories': open('/path/to/file.jpg', 'rb')}// CORRECT - PHP with CURLFile$file = new CURLFile('/path/to/file.jpg', 'image/jpeg', 'file.jpg');
Solution: Always use proper file upload mechanisms that send binary data, not text representations
Magic Bytes Validation Failure
Error:Invalid file content typeIssue: File extension doesn’t match actual file contentCommon Causes:
Renamed file with wrong extension (e.g., .txt renamed to .jpg)
Corrupted file
File created by unsupported software
Solution:
Use legitimate image editing or PDF software
Don’t just rename file extensions
Verify file opens correctly before uploading
Try converting file to correct format using standard tools
File Too Large Even After Compression
Error:File size exceeds 25MB limitSolutions:
For PDFs: Split multi-page documents across front/back/signature fields
For images: Reduce resolution (1920px width is usually sufficient)
For images: Convert to JPEG with 80-85% quality
For images: Use online compression tools like TinyPNG or Squoosh
Consider splitting document into multiple logical files
Rate Limit Issues During Bulk Upload
Error:429 Too Many RequestsIssue: Trying to upload files for multiple merchants quicklySolutions:
Implement rate limiting in your application (max 5 requests per 60s)
Add delays between merchant file uploads
Queue uploads and process with appropriate spacing
Use exponential backoff for retries
Copy
Ask AI
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));async function uploadForMultipleMerchants(merchantFileMap) { const results = []; for (const [merchantId, files] of Object.entries(merchantFileMap)) { try { const result = await uploadOnboardingFiles(merchantId, files); results.push({ merchantId, success: true, data: result }); } catch (error) { results.push({ merchantId, success: false, error: error.message }); } // Wait 12 seconds between uploads (5 requests per 60s = 1 per 12s) await delay(12000); } return results;}
Multipart Form Data Encoding Issues
Issue: Files not being received by server or showing as undefinedSolutions for Different Languages:Node.js:
Copy
Ask AI
// Use form-data package, not URLSearchParamsconst FormData = require('form-data');const form = new FormData();form.append('idsOfValidSignatories', fs.createReadStream(filePath));// Include form headersaxios.post(url, form, { headers: { 'Authorization': `Bearer ${token}`, ...form.getHeaders() // Important! }});
Python:
Copy
Ask AI
# Let requests handle Content-Type headerfiles = {'idsOfValidSignatories': open(file_path, 'rb')}headers = {'Authorization': f'Bearer {token}'}# Don't set Content-Type manuallyrequests.post(url, headers=headers, files=files)
PHP:
Copy
Ask AI
// Use CURLFile, not @ syntax (deprecated)$file = new CURLFile($filePath, 'image/jpeg', 'filename.jpg');
File Name Sanitization Concerns
Issue: Uploaded file name doesn’t match originalExplanation: File names are sanitized for security:
Special characters removed
Path traversal sequences removed (../, ..)
Length truncated to 255 characters
Best Practice:
Use simple, descriptive file names
Avoid special characters
Use the returned id field to reference files, not name
Store your own mapping of file IDs to original names if needed