Connect Microsoft 365 / Outlook accounts to send emails through DataPublisher using Microsoft Graph API.
---
https://app.datapublisher.io/api/email/auth
---
All endpoints require JWT authentication via Authorization: Bearer header.
---
The Email Authentication API allows you to:
-Store and refresh access tokens securely
Supported Providers:
Required Scopes:
Mail.Send - Send emails as user
offline_access - Refresh tokens
User.Read - Get user email address
---
Start the OAuth2 authorization flow.
Endpoint: GET /microsoft/connect
Headers:
Authorization: Bearer
Response (200 OK):
{
"authUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=...&redirect_uri=...&response_type=code&scope=Mail.Send%20offline_access%20User.Read&state=abc123def456"
}
Usage:
authUrl in browser or Office dialog
---
Microsoft redirects to this endpoint after authorization. This is called automatically by Microsoft - you don't need to call it directly.
Endpoint: GET /microsoft/callback
Query Parameters (from Microsoft):
code: Authorization code
state: State parameter for CSRF protection
Response (200 OK):
Process:
access_token and refresh_token encrypted in database
Database Storage:
email_providers table:
- user_id: User UUID
- provider: 'microsoft'
- email_address: User's email
- access_token: Encrypted access token
- refresh_token: Encrypted refresh token (for auto-renewal)
- token_expires_at: Token expiration timestamp
- scopes: JSON array of granted scopes
- is_active: Connection status
---
List all connected email accounts for the user.
Endpoint: GET /providers
Headers:
Authorization: Bearer
Response (200 OK):
{
"success": true,
"providers": [
{
"id": "uuid",
"provider": "microsoft",
"emailAddress": "user@company.com",
"isActive": true,
"tokenExpiresAt": "2026-02-13T10:00:00Z",
"scopes": ["Mail.Send", "offline_access", "User.Read"],
"connectedAt": "2026-02-01T09:00:00Z",
"lastUsedAt": "2026-02-12T08:30:00Z"
}
],
"count": 1
}
---
Remove email account connection and revoke tokens.
Endpoint: DELETE /providers/:providerId
Headers:
Authorization: Bearer
Response (200 OK):
{
"success": true,
"message": "Email account disconnected successfully"
}
Notes:
---
Access tokens automatically refresh when expired. This happens invisibly when sending emails.
Process:
refresh_token to request new access_token
Token Lifetimes:
---
Endpoint: POST /send
Headers:
Authorization: Bearer
Content-Type: application/json
Request Body:
{
"providerId": "uuid",
"to": ["recipient@example.com"],
"cc": ["cc@example.com"],
"bcc": ["bcc@example.com"],
"subject": "Email Subject",
"bodyHtml": "Hello
Email content
",
"bodyText": "Hello\n\nEmail content (plain text fallback)",
"attachments": [
{
"name": "document.pdf",
"contentBytes": "base64-encoded-file-content",
"contentType": "application/pdf"
}
],
"importance": "normal",
"trackingId": "optional-tracking-uuid"
}
Response (200 OK):
{
"success": true,
"messageId": "microsoft-message-id",
"sentAt": "2026-02-12T10:30:00Z"
}
Error Responses:
401 Unauthorized - Token expired or invalid:
{
"success": false,
"error": "Email account not connected or token expired"
}
400 Bad Request - Missing required fields:
{
"success": false,
"error": "Missing required fields: to, subject, bodyHtml"
}
---
Only requests necessary scopes:
Mail.Send - Send emails (required)
offline_access - Get refresh tokens (required)
User.Read - Read email address (minimal profile access)
Does NOT request:
---
// In Office Add-in task pane
async function connectEmailAccount() {
try {
// Get auth URL from API
const response = await fetch('https://app.datapublisher.io/api/email/auth/microsoft/connect', {
headers: {
'Authorization': Bearer ${jwtToken}
}
});
const { authUrl } = await response.json();
// Open auth dialog (Office.js)
Office.context.ui.displayDialogAsync(authUrl, { height: 60, width: 30 }, (result) => {
const dialog = result.value;
// Listen for success message from dialog
dialog.addEventHandler(Office.EventType.DialogMessageReceived, (arg) => {
const message = JSON.parse(arg.message);
if (message.status === 'success') {
console.log('Email connected:', message.email);
dialog.close();
// Refresh email provider list
loadEmailProviders();
}
});
});
} catch (error) {
console.error('Error connecting email:', error);
}
}
---
POST https://login.microsoftonline.com/common/oauth2/v2.0/token
GET https://login.microsoftonline.com/common/oauth2/v2.0/authorize
POST https://graph.microsoft.com/v1.0/me/sendMail
---
Cause: Using MSAL.js client library (doesn't expose refresh tokens)
Solution: Direct OAuth2 token endpoint call to get refresh token
Cause: Organization requires admin approval for Mail.Send scope
Solution: IT admin must pre-approve the app in Azure portal
Cause: User hasn't used email for 90+ days, refresh token expired
Solution: User must re-connect email account
---
Required environment variables:
Azure App Registration
EMAIL_CLIENT_ID=your-client-id
EMAIL_CLIENT_SECRET=your-client-secret
EMAIL_TENANT_ID=common # Or specific tenant ID
Redirect URI (must match Azure app registration)
SERVER_URL=https://app.datapublisher.io
Token encryption
ENCRYPTION_KEY=32-byte-hex-string
---
---
| Status Code | Description |
|-------------|-------------|
| 400 | Missing required parameters or invalid request |
| 401 | Unauthorized - invalid JWT or expired email token |
| 403 | Insufficient Microsoft permissions |
| 404 | Email provider not found |
| 429 | Too many requests (Microsoft throttling) |
| 500 | Internal server error or Microsoft API error |
---
---