Use Tab, then Enter to open a result.
Identity verification remains a friction point in digital onboarding. Traditional chat-based document collection often results in poor image quality, incorrect file types, and fragmented data. WhatsApp Flows solve this by providing a structured interface for data entry. Implementing a WhatsApp Flows identity verification document upload system enables businesses to collect Know Your Customer (KYC) data within a single, controlled environment.
This article details the architectural requirements and implementation steps for building a secure document upload workflow. It focuses on the technical challenges of handling binary data, managing media life cycles, and ensuring data integrity during the transition from the WhatsApp client to your private storage.
The Architecture of Structured Document Collection
When a user uploads a document through a Flow, the process differs from standard media messaging. In a standard chat, the user sends an image, and your webhook receives a notification. In a Flow, the document is an integrated component of a larger form. This structure requires a two-phase handling strategy. First, the Flow client handles the local file selection and initial upload to Meta servers. Second, your backend processes the resulting media reference to move the file into your secure environment.
This approach prevents your server from handling raw multi-part form data directly from thousands of concurrent users. Instead, you interact with stable media identifiers. However, this introduces a dependency on the Meta Media API. You must design your system to handle the ephemeral nature of these identifiers. Links provided by the API typically expire within a few hours. If your verification logic includes manual review, you must move the file to a persistent bucket immediately.
Prerequisites for Implementation
Before starting the implementation, ensure your environment meets these requirements:
- A WhatsApp Business Account (WABA) with Cloud API access.
- A verified Business Manager to enable document upload components in Flows.
- A backend server capable of handling HTTPS POST requests with valid SSL certificates.
- Secure storage such as Amazon S3, Google Cloud Storage, or a private file server.
- Cryptographic libraries for handling RSA-OAEP encryption if you use endpoint-level encryption for Flow data.
Step 1: Designing the Flow JSON Schema
The Flow JSON defines the user interface. For identity verification, you use the MediaUpload component. This component supports specific file types and size limits. You must define these constraints in the schema to prevent users from uploading invalid formats.
{
"version": "3.1",
"screens": [
{
"id": "KYC_UPLOAD",
"title": "Identity Verification",
"data": {},
"terminal": false,
"layout": {
"type": "SingleColumnLayout",
"children": [
{
"type": "TextHeading",
"text": "Upload National ID"
},
{
"type": "TextBody",
"text": "Please provide a clear photo of the front of your ID card."
},
{
"type": "MediaUpload",
"name": "national_id_front",
"label": "Select Image",
"file_types": ["image/jpeg", "image/png", "application/pdf"],
"max_size_mb": 5,
"required": true
},
{
"type": "Footer",
"label": "Submit Documents",
"on_click_action": {
"name": "data_exchange",
"payload": {
"id_media_id": "${form.national_id_front}",
"action": "submit_kyc"
}
}
}
]
}
}
]
}
This schema creates a screen with a file picker. When the user selects a file, the client prepares it for the data_exchange action. The payload sends the media_id rather than the file itself. This reduces the payload size for the initial webhook notification.
Step 2: Handling the Webhook Data Exchange
When the user clicks the footer button, WhatsApp sends a POST request to your configured endpoint. This request contains an encrypted payload. Your first task is to decrypt this data to extract the media_id.
If you use the official Cloud API, the payload follows a specific structure. If you use an alternative like WASenderApi for high-volume management of standard accounts, the media handling logic remains similar but requires different authentication headers. For official Flows, your server must respond with a structure that tells the Flow client what to do next. For example, it might show a success screen or an error message if the file extension is incorrect.
{
"action": "data_exchange",
"data": {
"id_media_id": "123456789012345",
"action": "submit_kyc",
"user_context": "customer_99"
}
}
Your backend logic should immediately queue a background task to fetch the media. Do not attempt to download the file during the synchronous execution of the webhook response. High-latency file operations will cause the Flow to time out on the user device.
Step 3: Media Retrieval and Permanent Storage
Retrieving the document requires a GET request to the Meta Media API. This returns a JSON object containing a temporary URL. You then perform a second GET request to that URL to stream the binary data.
This Node.js example demonstrates the logic for downloading the file and streaming it to a storage provider. This script avoids loading the entire file into memory, which prevents heap overflows when handling large documents or multiple concurrent uploads.
const axios = require('axios');
const fs = require('fs');
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
async function processKycDocument(mediaId, accessToken) {
try {
// Fetch the temporary URL from the Media API
const metaResponse = await axios.get(`https://graph.facebook.com/v21.0/${mediaId}`, {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
const downloadUrl = metaResponse.data.url;
// Stream the file from Meta to your S3 bucket
const fileStream = await axios({
method: 'get',
url: downloadUrl,
responseType: 'stream',
headers: { 'Authorization': `Bearer ${accessToken}` }
});
const s3 = new S3Client({ region: 'us-east-1' });
const uploadParams = {
Bucket: 'my-secure-kyc-documents',
Key: `uploads/${mediaId}.jpg`,
Body: fileStream.data,
ContentType: 'image/jpeg'
};
await s3.send(new PutObjectCommand(uploadParams));
return { success: true, key: uploadParams.Key };
} catch (error) {
console.error('Document processing failed:', error.message);
throw error;
}
}
Security and Data Integrity Considerations
Handling PII (Personally Identifiable Information) on WhatsApp requires strict security controls. Standard WhatsApp messages use end-to-end encryption. However, once the data reaches your webhook, the responsibility for protection shifts to your infrastructure.
Endpoint Encryption
WhatsApp Flows support an additional layer of encryption. You provide a public key in the Flow configuration. WhatsApp encrypts the data_exchange payload using this key. Your server decrypts it using your private key. This ensures that even if the network traffic is intercepted, the document identifiers remain protected. Use the RSA-OAEP padding scheme for this implementation.
Temporary Storage Hazards
Never store these documents in public-facing web folders. Access should require short-lived signed URLs. If your identity verification process involves third-party OCR services, transmit the data via secure tunnels or VPC peering. Avoid logging the media_id or the download URL in plaintext application logs. These identifiers provide enough access for unauthorized parties to retrieve the document if they possess the WABA access token.
Identifying and Fixing Silent Failures
Systems often fail in production due to environmental factors that do not appear during local testing. In the context of document uploads, three specific issues occur frequently.
1. Payload Mismatch during Decryption
If the encryption configuration on the WhatsApp Manager does not match your backend implementation, the decryption will fail. This usually results in a generic 500 Internal Server Error sent back to the Flow client. The user sees a message stating the service is unavailable. Always verify the x-hub-signature-256 header to ensure the request originated from Meta before attempting decryption.
2. Media API Rate Limiting
If you process thousands of KYC documents in a short window, you might hit rate limits on the Media API. These limits apply to the WABA as a whole. Implementing a retry strategy with exponential backoff for the media metadata request is necessary. If the initial GET request to graph.facebook.com returns a 429 status code, wait before retrying.
3. File Truncation
Large documents might result in incomplete streams if your server closes the connection too early. Ensure your streaming logic handles the end and error events of the response stream. Verify the Content-Length header against the size of the stored file to ensure the full binary transferred correctly.
Troubleshooting Common Issues
- Error: Media ID not found. This occurs if you attempt to fetch the media too long after the user submitted the flow. Media IDs are volatile. Process them within minutes of receipt.
- Error: Unsupported File Type. Even if your Flow JSON specifies certain types, the user might try to upload unsupported variants if the client app version is outdated. Always validate the MIME type on the backend after the download starts.
- Flow Timeout on Device. This is usually caused by a slow synchronous response from your webhook. Ensure your webhook returns a 200 OK with the next Flow screen JSON within 3 seconds. Offload the media download to a background worker.
FAQ
What is the maximum file size for document uploads in WhatsApp Flows? The current limit for media components in Flows is typically 5MB per file. For larger documents, you must redirect users to an external web-based upload portal, though this increases friction and abandonment rates.
Do users need a specific version of WhatsApp to use document uploads? Yes. Document upload components require recent versions of the WhatsApp client. If a user has an older version, the Flow might not render or the component will appear as an unsupported element. You should provide a fallback text message with an alternative upload link.
Can I collect multiple documents in a single Flow screen?
You are able to include multiple MediaUpload components on one screen. However, this increases the risk of a timeout if the user has a slow uplink. It is often more reliable to split the upload process across two screens: one for the front of the ID and another for the back.
Is it possible to use WASenderApi for document flows? WASenderApi works well for triggering notifications and handling incoming media in standard chat. However, the interactive "Flows" feature is a specific Meta Business API technology. If you use WASenderApi, you typically handle document collection through a guided chat sequence rather than a structured JSON flow. This requires different logic to track state and ensure the user sends the correct file.
How do I handle blurry or unreadable documents? Integrated OCR (Optical Character Recognition) is the best solution. Once your backend stores the file, pass it to an OCR engine. If the confidence score is low, use a WhatsApp template to message the user automatically and ask for a clearer photo. This maintains the conversation loop without manual intervention.
Conclusion and Next Steps
Implementing document uploads within WhatsApp Flows creates a seamless onboarding experience. By moving from unstructured chat to a defined JSON schema, you improve data quality and reduce processing time. The key to a successful deployment lies in the backend architecture. You must prioritize asynchronous processing, secure binary streaming, and robust error handling to manage the ephemeral nature of Meta media references.
To move forward, begin by testing a single-component Flow with a simple storage backend. Once the media retrieval logic is stable, implement RSA-OAEP encryption to protect user data. Finally, integrate automated validation to provide real-time feedback to users who upload incorrect or unreadable documents. This methodical approach ensures your identity verification system remains reliable at scale.