Forum Discussion

rcant's avatar
rcant
Copper Contributor
Oct 03, 2025

403 Forbidden when sending mail with app-only token via Microsoft Graph

Hello,

I am trying to send emails from my Outlook account using a registered enterprise application in Azure AD.

We created an application registration in our tenant, assigned the relevant users, and granted admin consent for these Microsoft Graph application permissions: Mail.Send and Mail.ReadWrite and Mail.Send.Shared.

I authenticate with application credentials (client_id, client_secret, tenant_id) and successfully retrieve an app-only access token using MSAL in Python:

def get_access_token() -> str:
    load_dotenv()
    client_id = os.getenv("CLIENT_ID")
    client_secret = os.getenv("CLIENT_SECRET")
    tenant_id = os.getenv("TENANT_ID")
    
    authority = f"https://login.microsoftonline.com/{tenant_id}"
    scopes = ["https://graph.microsoft.com/.default"]  # app-only token

    app = msal.ConfidentialClientApplication(
        client_id=client_id,
        client_credential=client_secret,
        authority=authority
    )

    result = app.acquire_token_for_client(scopes=scopes)
    
    if "access_token" not in result:
        raise RuntimeError(f"Auth failed: {result.get('error_description') or result}")

    return result["access_token"]

The token is retrieved successfully. However, when I try to send an email with:

GRAPH_BASE = "https://graph.microsoft.com/v1.0"

def send_email(access_token: str, from_user: str, to_address: str, subject: str, body_text: str, save_to_sent: bool = True) -> bool:
    """
    Sends a plain-text email via POST /users/{from_user}/sendMail using an app-only token.
    Returns True on success; raises HTTPError on failure.
    """
    payload = {
        "message": {
            "subject": subject,
            "body": {"contentType": "Text", "content": body_text},
            "toRecipients": [{"emailAddress": {"address": to_address}}],
        },
        "saveToSentItems": bool(save_to_sent),
    }
    r = requests.post(
        f"{GRAPH_BASE}/users/{from_user}/sendMail",
        headers={"Authorization": f"Bearer {access_token}"},
        json=payload,
        timeout=20,
    )
    r.raise_for_status()
    return True

…I get this error:

403 Client Error: Forbidden for url: https://graph.microsoft.com/v1.0/users/{from_user}/sendMail

File "C:\mail\src\mail.py", line 53, in send_email r.raise_for_status() ~~~~~~~~~~~~~~~~~~^^ File "C:\mail\src\mail.py", line 111, in <module> send_email(token, from_user, to, "Hello from Microsoft Graph", "Hello Human") ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://graph.microsoft.com/v1.0/users/{from_user}/sendMail

where {from_user} is my actual mailbox address (e.g., email address removed for privacy reasons).

Since the app has Mail.Send (Application) permission with admin consent, my understanding is that the app should be able to send mail on behalf of any user in the tenant using /users/{user}/sendMail.

Is there another configuration step I am missing (e.g., Application Access Policy or mailbox-level Send As requirement)? Any guidance on why this 403 happens despite having Mail.Send application permissions with admin consent would be very helpful.

Thank you!

No RepliesBe the first to reply

Resources