Base URL
https://app.dmu.email/api/v1
Authentication
Bearer dmk_… API key
Rate limit
120 requests a minute per key

Get a message

GET/messages/{id}

Returns one message with its attachments and, for mail you sent, how the send went.

Needs the permissionmessages:read

This is where you find out a send failed: status goes from queued to sending to sent, or ends at failed with lastError holding what the receiving server said. To be told rather than poll, subscribe a webhook to message.sent and message.failed.

sending means the send began and how it ended isn’t known yet. It isn’t counted as sent, because a message that has been sending for an hour needs looking at.

Path parameters

idstringrequired
The message. A draft keeps its id when it’s sent, so a draft’s id works here too.

Returns

idstring
The message.
threadIdstring
The conversation it’s in.
subjectstring
Its subject.
fromobject
The sender, as address and name.
tolist of strings
The recipients.
bodyHtmlstring
Sanitised, but still written by the sender: show it in a sandboxed iframe. bodyText is the plain version.
sentAtstring or null
When it was sent or arrived. Null for a draft.
scheduledAtstring or null
When a scheduled send will go.
statusstring or null
For mail written in DMU Mail: draft, queued, sending, sent or failed. Null for mail that arrived.
lastErrorstring or null
Why the last attempt to send it failed.
attachmentslist
Its files, each with id, filename, mimeType and size in bytes. See Download an attachment.

Errors

401invalid_key
The key is wrong, expired or revoked. With no Authorization header at all, the code is missing_credentials.
403missing_scope
The key doesn’t have messages:read. The X-Required-Scope header names what’s missing.
404not_found
There’s no such message, or the key’s owner can’t see it.
429rate_limited
More than 120 requests a minute from this key. Wait for Retry-After seconds.
curl https://app.dmu.email/api/v1/messages/8c1e2f4a \
  -H "Authorization: Bearer dmk_7f3a9c2e1b4d_Xq8v…"

Response

200 OK

{
  "data": {
    "id": "8c1e2f4a",
    "subject": "Re: Invoice INV-2026-0841",
    "from": {
      "address": "accounts@aegeanfreight.gr",
      "name": "Aegean Freight"
    },
    "to": ["maria@example.com"],
    "bodyHtml": "<p>Thank you, received.</p>",
    "bodyText": "Thank you, received.",
    "sentAt": "2026-09-27T09:02:31.000Z",
    "scheduledAt": null,
    "status": "sent",
    "threadId": "a41c93f0",
    "lastError": null,
    "attachments": [
      {
        "id": "1f6c8d33",
        "filename": "receipt.pdf",
        "mimeType": "application/pdf",
        "size": 84213
      }
    ]
  }
}

Every response wraps its result in data. A page of threads adds nextCursor, which is null on the last page.