WhatsApp API Authentication Errors: Expired Token and Permissions
How to fix WhatsApp Business API authentication errors: codes 0, 3, 190 and access token issues
Common Authentication Errors
Authentication errors are the first obstacles you encounter when integrating with the WhatsApp Business API. Here are the main ones and how to fix them.
Error 190: Expired Token
{
"error": {
"message": "Error validating access token: Session has expired",
"type": "OAuthException",
"code": 190,
"error_subcode": 463,
"fbtrace_id": "..."
}
}
Cause
The temporary access token has expired. Test tokens from Meta for Developers last only 24 hours.
Solution 1: Generate Permanent Token
- Go to Meta Business Suite
- Settings > Users > System user access tokens
- Generate a token with required permissions
// Example usage with permanent token
const PERMANENT_TOKEN = process.env.WHATSAPP_TOKEN;
async function sendMessage(to, message) {
const response = await fetch(
`https://graph.facebook.com/v18.0/${phoneNumberId}/messages`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${PERMANENT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messaging_product: 'whatsapp',
to,
type: 'text',
text: { body: message }
})
}
);
return response.json();
}
Solution 2: Implement Automatic Refresh
For applications using OAuth:
class WhatsAppClient {
constructor() {
this.accessToken = process.env.WHATSAPP_TOKEN;
this.appId = process.env.APP_ID;
this.appSecret = process.env.APP_SECRET;
}
async refreshAccessToken() {
const response = await fetch(
`https://graph.facebook.com/v18.0/oauth/access_token?` +
`grant_type=fb_exchange_token&` +
`client_id=${this.appId}&` +
`client_secret=${this.appSecret}&` +
`fb_exchange_token=${this.accessToken}`
);
const data = await response.json();
if (data.access_token) {
this.accessToken = data.access_token;
await this.saveToken(data.access_token);
}
return data;
}
async request(url, options = {}) {
try {
const response = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${this.accessToken}`
}
});
const data = await response.json();
if (data.error?.code === 190) {
await this.refreshAccessToken();
return this.request(url, options);
}
return data;
} catch (error) {
throw error;
}
}
}
Error 0: AuthException
{
"error": {
"message": "Invalid OAuth access token",
"type": "OAuthException",
"code": 0,
"fbtrace_id": "..."
}
}
Cause
The token is invalid - may have been revoked, malformed, or never existed.
Solution
- Verify the token is correct (no extra spaces)
- Use the Access Token Debugger
- Generate a new token if needed
// Validate token before using
async function validateToken(token) {
const response = await fetch(
`https://graph.facebook.com/debug_token?input_token=${token}&access_token=${token}`
);
const data = await response.json();
if (data.data?.is_valid) {
console.log('Token valid until:', new Date(data.data.expires_at * 1000));
return true;
}
console.log('Token invalid:', data.data?.error?.message);
return false;
}
Error 3: Insufficient Permissions
{
"error": {
"message": "(#3) Application does not have permission for this action",
"type": "OAuthException",
"code": 3,
"fbtrace_id": "..."
}
}
Cause
The token doesn't have the necessary permissions for the action.
Required Permissions
For WhatsApp Business API, you need:
| Permission | Use |
|------------|-----|
| whatsapp_business_management | Manage account and settings |
| whatsapp_business_messaging | Send and receive messages |
| business_management | Manage Business Manager |
Solution
- Go to Meta for Developers > your App > App Review
- Request the necessary permissions
- Generate a new token with approved permissions
// Check token permissions
async function checkPermissions(token) {
const response = await fetch(
`https://graph.facebook.com/me/permissions?access_token=${token}`
);
const data = await response.json();
const permissions = data.data.filter(p => p.status === 'granted');
console.log('Granted permissions:', permissions.map(p => p.permission));
const required = [
'whatsapp_business_management',
'whatsapp_business_messaging'
];
const missing = required.filter(
r => !permissions.find(p => p.permission === r)
);
if (missing.length > 0) {
console.log('Missing permissions:', missing);
return false;
}
return true;
}
Secure Token Configuration
Environment Variables
# .env
WHATSAPP_TOKEN=your_token_here
WHATSAPP_PHONE_NUMBER_ID=123456789
WHATSAPP_BUSINESS_ACCOUNT_ID=987654321
META_APP_SECRET=app_secret_here
Never do this:
// ❌ WRONG - Token exposed in code
const token = 'EAABsbCS1iHgBAKm...';
// ❌ WRONG - Token in logs
console.log('Token:', process.env.WHATSAPP_TOKEN);
Do this instead:
// ✅ CORRECT - Token in environment variable
const token = process.env.WHATSAPP_TOKEN;
// ✅ CORRECT - Masked log
console.log('Token:', token ? '***configured***' : 'NOT CONFIGURED');
Authentication Middleware
class WhatsAppAuthMiddleware {
constructor(config) {
this.token = config.token;
this.phoneNumberId = config.phoneNumberId;
this.lastValidation = null;
this.validationInterval = 60 * 60 * 1000; // 1 hour
}
async validateIfNeeded() {
const now = Date.now();
if (!this.lastValidation ||
now - this.lastValidation > this.validationInterval) {
const isValid = await this.validateToken();
if (!isValid) {
throw new Error('WhatsApp token invalid or expired');
}
this.lastValidation = now;
}
}
async validateToken() {
try {
const response = await fetch(
`https://graph.facebook.com/v18.0/${this.phoneNumberId}`,
{
headers: {
'Authorization': `Bearer ${this.token}`
}
}
);
const data = await response.json();
return !data.error;
} catch {
return false;
}
}
}
Conclusion
Authentication errors are usually:
- Expired token: Use permanent tokens or implement refresh
- Invalid token: Check with Debug Token
- Missing permissions: Request via App Review
Keep your tokens secure and implement periodic validation to avoid production failures.
Need help with API authentication? Contact me for consulting.