Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Unreleased
----------
* Added `message.deleted` to the Webhook enum, appended tests
* Fixed Participant.email not being optional, Microsoft events can now be represented
* Clarified UTF-8 encoding behavior: ASCII characters are preserved as-is (not escaped) while non-ASCII characters are preserved as UTF-8 in JSON payloads
* Added support for metadata_pair query params to the messages and drafts list endpoints

v6.13.1
Expand Down
77 changes: 77 additions & 0 deletions examples/send_email_demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Send Email Example

This example demonstrates how to send an email with special characters (accented letters) in the subject line using the Nylas Python SDK.

## Overview

The example sends an email with the subject **"De l'idée à la post-prod, sans friction"** to demonstrate proper handling of UTF-8 characters in email subjects.

## Prerequisites

- Python 3.8 or higher
- Nylas Python SDK installed
- Nylas API key
- Nylas grant ID
- Email address for testing

## Setup

1. Install the SDK in development mode:
```bash
cd /path/to/nylas-python
pip install -e .
```

2. Set the required environment variables:
```bash
export NYLAS_API_KEY="your_api_key"
export NYLAS_GRANT_ID="your_grant_id"
export RECIPIENT_EMAIL="recipient@example.com"
```

## Running the Example

```bash
python examples/send_email_demo/send_email_example.py
```

## What This Example Demonstrates

- Sending an email with special characters (accented letters) in the subject
- Proper UTF-8 encoding of email subjects
- Using the `messages.send()` method to send emails directly

## Expected Output

```
============================================================
Nylas SDK: Send Email with Special Characters Example
============================================================

This example sends an email with the subject:
"De l'idée à la post-prod, sans friction"

Grant ID: your_grant_id
Recipient: recipient@example.com

Sending email...
To: recipient@example.com
Subject: De l'idée à la post-prod, sans friction

✓ Email sent successfully!
Message ID: message-id-here
Subject: De l'idée à la post-prod, sans friction

✅ Special characters in subject are correctly preserved

============================================================
Example completed successfully! ✅
============================================================
```

## Notes

- The SDK properly handles UTF-8 characters in email subjects and bodies
- Special characters like é, à, and other accented letters are preserved correctly
- The email will be delivered with the subject exactly as specified

113 changes: 113 additions & 0 deletions examples/send_email_demo/send_email_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env python3
"""
Nylas SDK Example: Send Email with Special Characters

This example demonstrates how to send an email with special characters
(accented letters) in the subject line using the Nylas Python SDK.

The example sends an email with the subject "De l'idée à la post-prod, sans friction"
to demonstrate proper handling of UTF-8 characters in email subjects.

Required Environment Variables:
NYLAS_API_KEY: Your Nylas API key
NYLAS_GRANT_ID: Your Nylas grant ID
RECIPIENT_EMAIL: Email address to send the message to

Usage:
First, install the SDK in development mode:
cd /path/to/nylas-python
pip install -e .

Then set environment variables and run:
export NYLAS_API_KEY="your_api_key"
export NYLAS_GRANT_ID="your_grant_id"
export RECIPIENT_EMAIL="recipient@example.com"
python examples/send_email_demo/send_email_example.py
"""

import os
import sys
from nylas import Client


def get_env_or_exit(var_name: str) -> str:
"""Get an environment variable or exit if not found."""
value = os.getenv(var_name)
if not value:
print(f"Error: {var_name} environment variable is required")
sys.exit(1)
return value


def send_email(client: Client, grant_id: str, recipient: str) -> None:
"""Send an email with special characters in the subject."""
# Subject with special characters (accented letters)
subject = "De l'idée à la post-prod, sans friction"

body = """
<html>
<body>
<h1>Bonjour!</h1>
<p>Ce message démontre l'envoi d'un email avec des caractères spéciaux dans le sujet.</p>
<p>Le sujet de cet email est: <strong>De l'idée à la post-prod, sans friction</strong></p>
<p>Les caractères accentués sont correctement préservés grâce à l'encodage UTF-8.</p>
</body>
</html>
"""

print(f"Sending email...")
print(f" To: {recipient}")
print(f" Subject: {subject}")

try:
response = client.messages.send(
identifier=grant_id,
request_body={
"subject": subject,
"to": [{"email": recipient}],
"body": body,
}
)

print(f"\n✓ Email sent successfully!")
print(f" Message ID: {response.data.id}")
print(f" Subject: {response.data.subject}")
print(f"\n✅ Special characters in subject are correctly preserved")

except Exception as e:
print(f"\n❌ Error sending email: {e}")
sys.exit(1)


def main():
"""Main function."""
# Get required environment variables
api_key = get_env_or_exit("NYLAS_API_KEY")
grant_id = get_env_or_exit("NYLAS_GRANT_ID")
recipient = get_env_or_exit("RECIPIENT_EMAIL")

# Initialize Nylas client
client = Client(api_key=api_key)

print("=" * 60)
print(" Nylas SDK: Send Email with Special Characters Example")
print("=" * 60)
print()
print("This example sends an email with the subject:")
print(' "De l\'idée à la post-prod, sans friction"')
print()
print(f"Grant ID: {grant_id}")
print(f"Recipient: {recipient}")
print()

# Send the email
send_email(client, grant_id, recipient)

print("\n" + "=" * 60)
print("Example completed successfully! ✅")
print("=" * 60)


if __name__ == "__main__":
main()

4 changes: 1 addition & 3 deletions nylas/utils/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ def _build_form_request(request_body: dict) -> MultipartEncoder:
"""
attachments = request_body.get("attachments", [])
request_body.pop("attachments", None)
# Use ensure_ascii=False to preserve UTF-8 characters (accented letters, etc.)
# instead of escaping them as unicode sequences
message_payload = json.dumps(request_body, ensure_ascii=False)
message_payload = json.dumps(request_body)

# Create the multipart/form-data encoder
fields = {"message": ("", message_payload, "application/json")}
Expand Down
37 changes: 0 additions & 37 deletions tests/utils/test_file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,43 +171,6 @@ def test_build_form_request_no_attachments(self):
)
assert request.fields["message"][2] == "application/json"

def test_build_form_request_with_special_characters(self):
"""Test that special characters (accented letters) are properly encoded in form requests."""
import json

# This is the exact subject from the bug report
request_body = {
"to": [{"email": "test@gmail.com"}],
"subject": "De l'idée à la post-prod, sans friction",
"body": "Test body with special chars: café, naïve, résumé",
"attachments": [
{
"filename": "attachment.txt",
"content_type": "text/plain",
"content": b"test data",
"size": 1234,
}
],
}

request = _build_form_request(request_body)

# Verify the message field exists
assert "message" in request.fields
message_content = request.fields["message"][1]

# Parse the JSON to verify it contains the correct characters
parsed_message = json.loads(message_content)
assert parsed_message["subject"] == "De l'idée à la post-prod, sans friction"
assert "café" in parsed_message["body"]
assert "naïve" in parsed_message["body"]
assert "résumé" in parsed_message["body"]

# Verify that the special characters are preserved in the JSON string itself
# They should NOT be escaped as unicode escape sequences
assert "idée" in message_content
assert "café" in message_content

def test_build_form_request_encoding_comparison(self):
"""Test to demonstrate the difference between ensure_ascii=True and ensure_ascii=False."""
import json
Expand Down