If your Python app suddenly throws this error: exchangelib.errors.MalformedResponseError: Unknown failure in response. Code: 403 the issue is usually not Entra itself. In most cases, Exchange Web Services rejects the request because the OAuth setup is wrong, the app lacks the correct Exchange permission, mailbox access is blocked, or EWS is disabled.

This guide explains what causes the error, how to check your Entra and Exchange setup, and how to fix it step by step.
What Causes Entra API 403 Error in exchangelib
A 403 error means Exchange Online received your request but refused access. That usually happens for one of these reasons:
- Your app uses the wrong permission type
- Your token matches the wrong auth flow
- The mailbox is outside the app’s allowed Exchange scope
- EWS is disabled for the tenant, mailbox, or app
- Your code uses app-only access, but the app only has delegated permission
In exchangelib, Exchange sometimes returns a blank or incomplete error response. When that happens, the library shows:
MalformedResponseError: Unknown failure in response. Code: 403instead of a more direct authorization message.
First Identify the Access Model
Before changing anything, confirm which auth model your app uses.
You are using delegated access if:
- A real user signs in
- The app works on behalf of that signed-in user
- You use an interactive login flow
This model needs:
EWS.AccessAsUser.All
You are using app-only access if:
- Your script runs in the background
- You use
client_idandclient_secret - No user signs in during runtime
- The service connects directly to mailboxes
This model needs:
full_access_as_app
Common Cause in exchangelib
This pattern usually means app-only access:
credentials = OAuth2Credentials(
client_id=client_id,
client_secret=client_secret,
tenant_id=tenant_id
)If you use only client_id, client_secret, and tenant_id, but the app registration only has delegated EWS permission, Exchange will usually return 403.
That mismatch is one of the most common reasons this error appears.
Fix 1: Add the Correct EWS Permission in Entra
Open your app registration in the Entra admin center and check API permissions.
If your app uses delegated access
Add:
- Office 365 Exchange Online
- Delegated permission
EWS.AccessAsUser.All
Then grant consent if your tenant requires it.
If your app uses app-only access
Add:
- Office 365 Exchange Online
- Application permission
full_access_as_app
Then grant admin consent.
Fix 2: Match the Token to the Access Type
Do not mix delegated permissions with app-only code.
If your code runs as a daemon or backend service, your token must come from an application flow and your app must have full_access_as_app. If your code runs with a signed-in user, use delegated login and EWS.AccessAsUser.All. Microsoft clearly separates these two EWS OAuth models.
A lot of 403 errors happen because developers register one model but code the other.other.
Fix 3: Check Exchange Mailbox Access Scope
Even if the Entra permission looks correct, Exchange Online can still block mailbox access.
In many tenants, Exchange limits which mailboxes an app can open. So the app may be valid, consent may be granted, and the token may still fail because the mailbox is outside the allowed scope.
Check that:
- the service principal exists correctly in Exchange Online
- the app has the required Exchange application role
- the target mailbox is inside the allowed scope
If this part is missing, Exchange can return 403 even when everything in Entra looks fine.
Fix 4: Check Whether EWS Is Disabled
EWS access can be disabled at multiple levels:
- organization level
- mailbox level
- application level
So if the app registration looks correct but the request still fails, verify that EWS is actually enabled where your app needs it.
Typical checks include Exchange settings like:
Set-OrganizationConfig -EwsEnabled:$false
Set-CASMailbox -Identity [email protected] -EwsEnabled:$falseIf one of these restrictions is active, the request will fail no matter how clean the token is.
Fix 5: Verify the EWS Endpoint
For Exchange Online, the usual EWS endpoint is:
https://outlook.office365.com/EWS/Exchange.asmxIf your app points to the wrong endpoint, or if you combine an EWS request with the wrong token/resource flow, authentication can fail.
Fix 6: Stop Using Basic Auth
If your older script still uses username and password directly, update it now.
Basic authentication no longer works for EWS in Exchange Online. The working setup is:
- register the app in Entra
- use OAuth
- assign the correct EWS permission
- confirm Exchange-side mailbox access
Anything built on old basic auth logic will keep breaking.
Fix 7: Review Your exchangelib Access Type
If your code looks like this:
account = Account(
primary_smtp_address=username,
credentials=credentials,
config=config,
autodiscover=False,
access_type=IMPERSONATION
)make sure your Exchange configuration actually supports that access pattern.
A lot of older EWS examples rely on impersonation logic, but current Exchange Online setups often fail unless the app permission, mailbox scope, and Exchange-side access rules all line up correctly.
So if the code looks right but still throws 403, check the Exchange authorization side more carefully.
Quick Checklist to Fix Entra API 403 Issue
| Step | What to Do | Key Details |
|---|---|---|
| 1 | Identify the auth model | Check if a user signs in or if it runs as a background service using client_secret. If no user signs in, treat it as app-only access. |
| 2 | Check Entra API permissions | Use EWS.AccessAsUser.All for delegated access and full_access_as_app for app-only access. |
| 3 | Grant consent | Ensure user consent or admin consent is fully completed. |
| 4 | Check Exchange mailbox access | Confirm the app is allowed to access the target mailbox. |
| 5 | Verify EWS is enabled | Check organization-level, mailbox-level, and application-level EWS settings. |
| 6 | Inspect 403 response headers | Look for WWW-Authenticate, x-ms-diagnostics, and X-BackEndHttpStatus to identify the root cause. |
Example Scenario
Let’s say your code looks like this:
credentials = OAuth2Credentials(
client_id=client_id,
client_secret=client_secret,
tenant_id=tenant_id
)
config = Configuration(
credentials=credentials,
auth_type=OAUTH2,
service_endpoint="https://outlook.office365.com/EWS/Exchange.asmx",
)
account = Account(
primary_smtp_address=username,
credentials=credentials,
config=config,
autodiscover=False,
access_type=IMPERSONATION
)If this app registration only has delegated permission, the setup is wrong.
This code behaves like a background service, so it usually needs:
- Exchange Online application permission
full_access_as_app - admin consent
- valid mailbox scope in Exchange Online
- EWS enabled for that mailbox and tenant
That is the first place to check before changing anything else.
EWS Deprecation 2026: What You Need to Know
EWS in Exchange Online is on a countdown. It is scheduled for deprecation and full disablement in October 2026.
So if you are fixing this in a production environment, treat it as both a repair and a warning sign. You can still fix the 403 now, but long-term planning should move toward Microsoft Graph wherever possible.
That does not change the immediate fix, but it does affect how much effort you should invest in older EWS-based workflows.
FAQs
Is this an Entra API problem?
Usually no. The 403 normally comes from Exchange Online EWS authorization, not from Entra being down.
Why does exchangelib show MalformedResponseError instead of a clean auth error?
Because Exchange can return a 403 with an empty or incomplete response body, and exchangelib turns that into an unknown failure.
Which permission do I need for a background script?
For background or daemon-style EWS access, use full_access_as_app.
Can EWS still be disabled even if the app is correct?
Yes. Exchange can block EWS at the organization, mailbox, or application level.
