WhatsApp Cloud API · Production authentication · 2026
How to Create a Permanent WhatsApp Business API Access Token for Production
The token on Meta's WhatsApp API Setup page is fine for a quick test. It is not what should be running your CRM, chatbot, order alerts or n8n workflow. This guide walks through the system user setup that keeps a WhatsApp Cloud API integration authenticated for the long run, and the checks that save you from a 2 a.m. "messages stopped sending" call.
By On Cloud API team · Updated · Checked against Meta's access token documentation on the same date
Quick answer
To get a permanent WhatsApp Cloud API access token, create a system user in Meta Business Settings, assign it your Meta app and your WhatsApp Business Account (WABA), then click Generate token, choose Never as the expiry and select business_management, whatsapp_business_management and whatsapp_business_messaging. Test it with one small API call, then store it on your server, never in frontend code.
Here's how this usually goes wrong. You create a Meta app, add WhatsApp, copy the token from the API Setup page and send a test message. It works, so the job looks done.
The next morning, order confirmations aren't going out. Your Laravel queue is full of authorization errors, the n8n workflow that was fine yesterday is red, and the webhook is still receiving messages while nothing goes out.
Nothing is wrong with the code. Production was running on a test token, and test tokens expire in less than a day.
Test token vs production system user token
Both are Meta access tokens, and both go in the same Authorization: Bearer header. What differs is who they represent and how long they last.
| Token | Represents | Lifetime | Use it for |
|---|---|---|---|
| User access token | You, the person logged in | Short-lived; Meta says it expires in less than 24 hours | Quick tests only |
| System user access token | Your business's backend software | Long-lived; you choose the expiry when generating it | Your own production integration |
| Business integration system user token | One customer onboarded by a Tech Provider | Long-lived, scoped to that customer | Tech Providers using Embedded Signup |
Meta describes system users as identities for servers and automated services. That's the whole point: your backend shouldn't depend on an employee logging in and pasting a fresh token every morning.
What "permanent" actually means
When you generate a system user token, Business Settings asks you to set an expiry. At the time of writing the options are a fixed period (60 days) or Never. Pick Never and there's no scheduled expiry, so no daily token swaps.
That's not the same as "works forever no matter what". A permanent token stops working if:
- someone revokes it or generates a replacement and invalidates the old one,
- the system user loses access to the app or the WABA,
- a permission is removed, or
- the system user itself is deleted.
What you need before creating the token
- Admin access to the Meta Business Portfolio that owns your WhatsApp setup.
- A Meta app with the WhatsApp product added.
- A WhatsApp Business Account with your number already connected to Cloud API.
- Your WABA ID and Phone Number ID (both on the app's WhatsApp → API Setup page).
Step by step: create a permanent WhatsApp Cloud API access token
Meta renames menu items now and then, but the flow stays the same: system user → assign assets → generate token → pick permissions.
- Open the right business. Go to Meta Business Settings (business.facebook.com/settings) and switch to the portfolio that owns your WhatsApp app and WABA. If your personal account can see several businesses, double-check this.
- Create a system user. Go to Users → System users → Add. Give it a clear name such as
whatsapp-productionand make it an Admin system user. An Employee system user works too, but you'll have to grant each WABA to it one by one. - Assign your Meta app. With the system user selected, click Assign assets → Apps, pick your WhatsApp app and give it full control (Manage app).
- Assign your WhatsApp account. Still under Assign assets, open WhatsApp accounts, pick your WABA and give full control. Skip this step and your token will authenticate fine, then fail on every WABA request.
- Click Generate token. Select the same Meta app you just assigned.
- Set the expiry to Never. This is the "permanent" part. If your security policy requires rotation, choose 60 days instead and schedule the renewal.
- Select the permissions:
business_management,whatsapp_business_managementandwhatsapp_business_messaging. Leave everything else unticked. - Copy the token straight into your secrets store. Meta shows it once. Don't park it in a notes app, a screenshot or a Slack message.
- Verify before you deploy. Run the checks in the next section before you touch your live app.
Which permissions should you select?
Meta's current access token guide lists three permissions for system user tokens:
| Permission | What it covers |
|---|---|
whatsapp_business_messaging | Sending and receiving messages, registering numbers, media |
whatsapp_business_management | Managing the WABA: phone numbers, message templates, settings, webhook subscriptions |
business_management | Business portfolio access that system user setups rely on |
Some older tutorials only tick whatsapp_business_messaging. That's enough to send a message, but the first time your app tries to sync templates or list phone numbers, you'll get a permissions error. Select all three and nothing beyond them.
Verify and test the token before going live
1. Check it in the Access Token Debugger
Paste the token into Meta's Access Token Debugger. Confirm four things:
- Type: System User.
- App: the WhatsApp app you intended.
- Expires: Never (or the date you chose).
- Scopes: all three permissions listed above.
2. Confirm it can reach your WABA
curl -X GET \ "https://graph.facebook.com/<GRAPH_API_VERSION>/<WABA_ID>/phone_numbers" \ -H "Authorization: Bearer <SYSTEM_USER_ACCESS_TOKEN>"
If your number comes back, the token works, the system user can see the WABA and you've got the right Phone Number ID. If you get an error here, fix the asset assignment first. There's no point testing a message yet.
3. Send one template message
curl -X POST \
"https://graph.facebook.com/<GRAPH_API_VERSION>/<PHONE_NUMBER_ID>/messages" \
-H "Authorization: Bearer <SYSTEM_USER_ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"messaging_product": "whatsapp",
"to": "<YOUR_OWN_TEST_NUMBER>",
"type": "template",
"template": {
"name": "hello_world",
"language": { "code": "en_US" }
}
}'
Use a current Graph API version from Meta's changelog in place of <GRAPH_API_VERSION> (for example v25.0), and send the test to your own number first.
How to store a permanent token safely
Keep it in a server environment variable or a secrets manager:
WHATSAPP_ACCESS_TOKEN=your_system_user_access_token WHATSAPP_WABA_ID=your_waba_id WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
Never like this:
const accessToken = "EAA...REAL_PRODUCTION_TOKEN...";
- Backend only. Anything shipped to a browser or a mobile app can be read by the person using it.
- Keep it out of Git, including private repos and old commits.
- Mask it in logs; never print the full value.
- Limit who on the team can read production secrets.
- Write down how to revoke and replace it, so a leak is a 10-minute fix, not a panic.
Swap the test token for the permanent one without downtime
- Create and verify the new token first. Keep the old setup running until the new token passes both test calls.
- Update the value in one place: your
.env, secrets manager or hosting panel, not scattered across the code. - Restart everything that caches config. Laravel queue workers, PHP-FPM, Docker containers and Node.js processes can keep the old token in memory. In Laravel, run
php artisan config:clearand restart the queue workers. - Send one controlled message to an internal number.
- Watch the logs for an hour. Auth errors right after a swap almost always mean one service is still holding the old token.
Permanent WhatsApp token not working? Check these first
Before you generate yet another token, work down this chain. A new token with the same broken setup will fail in exactly the same way.
Token type → Permissions → App assigned → WABA assigned → Phone Number ID → Message payload
| What you see | Likely cause | Fix |
|---|---|---|
Error 190 (OAuthException) | Expired, revoked or test token | Check the token in the Debugger; replace any user token with a system user token |
Error 200 or 10 | Missing permission | Regenerate with all three permissions |
Error 100 | Wrong ID in the request | Check the Phone Number ID and WABA ID; never put +92… or +971… in the URL |
| Token valid, WABA call fails | WABA not assigned | Assign the WhatsApp account to the system user |
| Worked before the swap, fails after | Old token cached | Restart workers, containers and services |
| "Permanent" token suddenly dies | Revoked or access removed | Check the Debugger and the system user's assets in Business Settings |
Using the permanent token in n8n, Laravel, Node.js or Python
The token works the same in every stack. Only the HTTP client changes. In n8n, save it once in the Credentials section rather than pasting it into a Code or Set node. That way, when you rotate it, you change it in one place. Our WhatsApp Business API + n8n guide covers the full workflow, including triggers and webhooks.
In Laravel/PHP read it from .env, in Node.js from process.env, and in Python from os.environ. The token process is also the same whether your business is in Pakistan, the UAE, India, Bangladesh, the UK or the US. What changes by country is message pricing, not authentication.
Don't want to manage tokens at all?
If you connect your number through a Meta Tech Provider using Embedded Signup, the provider receives a business integration token scoped to your account. You don't generate, store or rotate anything yourself. On Cloud API is a Meta Verified Tech Provider on the official WhatsApp Cloud API, with a shared inbox, chatbot builder, campaigns and a REST API on top. See the On Cloud API overview if you'd rather skip the credential work.
Production token checklist
- The token is a system user token, not the one copied from API Setup.
- The system user has the right app and WABA assigned.
- The token has all three permissions:
business_management,whatsapp_business_management,whatsapp_business_messaging. - The Debugger shows the expected type, app and expiry.
- The
/phone_numberscall and one test template both work. - The token is stored server-side, out of Git and masked in logs.
- Workers and containers were restarted after the swap.
- The team knows how to revoke and replace it.
Frequently asked questions
How do I create a permanent WhatsApp Business API access token?
In Meta Business Settings, create a system user, assign it your Meta app and WhatsApp Business Account, then click Generate token, set the expiry to Never and select business_management, whatsapp_business_management and whatsapp_business_messaging.
How long does the WhatsApp Cloud API test token last?
The user access token from the API Setup page is short-lived. Meta says it expires in less than 24 hours, so it should only be used for quick tests.
Can a WhatsApp system user access token really be permanent?
Yes, in the sense that you can generate it with no expiry by choosing Never. It still stops working if it is revoked, if the system user loses access to the app or WhatsApp account, or if a permission is removed.
Which permissions does a WhatsApp system user token need?
Meta's access token guide lists business_management, whatsapp_business_management and whatsapp_business_messaging for system user tokens. Select those three and nothing more.
How do I check whether my token is a system user token?
Paste it into Meta's Access Token Debugger. It shows the token type, the app it belongs to, its expiry and the permissions it has.
Why is my permanent WhatsApp token valid but messages still fail?
A valid token does not mean the system user can reach your WhatsApp account. Check that the app and WABA are assigned to the system user, that all three permissions are present and that you are using the Phone Number ID, not the visible phone number.
What does WhatsApp API error 190 mean?
Error 190 is an OAuthException that usually means the access token has expired, been revoked or is invalid. It is the typical error when a test token was deployed to production.
Can I use the permanent token in n8n, Laravel, Node.js or Python?
Yes. The token works the same in every stack. Store it in n8n credentials or a server environment variable, never in frontend code or a Git repository.
Do I need a permanent token if I use a Tech Provider like On Cloud API?
Usually not. When you connect through a Tech Provider's Embedded Signup, the provider receives a business integration token scoped to your account, so you do not generate or store a token yourself.



