Documentation

Email Drafts API

Create an Outlook draft email in the calling user's own connected mailbox. The draft is NOT sent — the user reviews it in Outlook and sends from there.

Prerequisite

The calling user must have connected their Microsoft account at /settings/integrations. If they haven't, the endpoint returns 409 microsoft_not_connected with a link to the integration page.

Both surfaces (REST + MCP) accept the same request shape and produce a draft in the same Outlook Drafts folder.

POST /api/v1/email-drafts

Creates a draft in the calling user's Outlook Drafts folder. Auth: Authorization: Bearer avs_… or a JWT bearer token.

Request body

{
    "to": ["[email protected]"],
    "cc": ["[email protected]"],
    "bcc": ["[email protected]"],
    "subject": "Estimate ready for review",
    "htmlBody": "<p>Hi Alice,</p><p>…</p>",
    "attachments": [
        {
            "fileName": "estimate.pdf",
            "contentType": "application/pdf",
            "contentBase64": "JVBERi0xLjQK…"
        }
    ],
    "inReplyToMessageId": "AAMkAGI2…"
}

to, subject, htmlBody are required. All other fields are optional. When inReplyToMessageId is supplied the draft is created as a reply in the same Outlook conversation thread; the supplied to/cc/bcc overwrite Graph's reply defaults.

Reply ID format

inReplyToMessageId must be a Microsoft Graph message id — the long URL-safe-base64 id field returned by GET /me/messages (typically starts with AAMk, around 140 characters). It is not the RFC 2822 Internet Message-Id (the <…@mail.gmail.com> value in raw email headers). Passing the wrong format returns graph_error with ErrorInvalidIdMalformed.

Response (200)

{
    "draftId": "AAMkAGI2…",
    "attachmentErrors": []
}

attachmentErrors only contains entries when an individual attachment failed; the draft itself still exists.

Error responses

StatusCodeWhen
400invalid_requestMissing to, malformed email address, missing attachment fields, etc.
400too_many_recipientsSum of to+cc+bcc exceeds 100.
409microsoft_not_connectedUser has not connected Microsoft. Body includes connectUrl.
413payload_too_largeTotal attachment bytes exceed 10 MB.
429rate_limitedMore than 20 drafts/min for this user.
502graph_errorMicrosoft Graph rejected the request. Body includes graphStatus and graphMessage.

Limits

  • Total recipients (to + cc + bcc): 100
  • Attachments per draft: 25
  • Total attachment bytes: 10 MB
  • Subject length: 255 characters
  • HTML body size: 5 MB
  • Drafts per user per minute: 20

Example

curl -X POST https://app.avstackr.com/api/v1/email-drafts \
    -H "Authorization: Bearer avs_…" \
    -H "Content-Type: application/json" \
    -d '{"to":["[email protected]"],"subject":"Follow-up","htmlBody":"<p>Hi,</p>"}'

MCP tool: create_email_draft

The same operation is exposed as an MCP tool. Field names use snake_case (html_body, in_reply_to_message_id, content_base64) instead of camelCase, and the response shape mirrors the REST envelope without HTTP wrapping:

{
    "draft_id": "AAMkAGI2…",
    "attachment_errors": []
}

Error responses use { "error": "<code>", "message": "…" } with the same code vocabulary as the REST endpoint. microsoft_not_connected additionally returns connect_url.