List delivery events
/api/v1/email-logsEvery event recorded for your organization's mail: accepted, delivered, deferred, bounced, complained, opened, clicked, dropped.
Polling this is the simple way to follow a send. Receiving webhooks is the better one — it costs you no requests and tells you sooner.
Opens and clicks are not reliable, by nature. Open tracking depends on a remote image loading, which privacy proxies fetch on the recipient's behalf and many clients block entirely. Treat them as a weak signal about a population, never as proof about a person.
Request
/api/v1/email-logscurl -X GET 'https://app.mailyte.com/api/v1/email-logs' \
-H 'Authorization: Bearer mk_live_YOUR_API_KEY'const response = await fetch('https://app.mailyte.com/api/v1/email-logs', {
method: 'GET',
headers: {
Authorization: 'Bearer mk_live_YOUR_API_KEY',
},
});
const { data } = await response.json();import requests
response = requests.get(
"https://app.mailyte.com/api/v1/email-logs",
headers={"Authorization": "Bearer mk_live_YOUR_API_KEY"},
)
data = response.json()["data"]<?php
$response = Http::withToken('mk_live_YOUR_API_KEY')
->get('https://app.mailyte.com/api/v1/email-logs');
$data = $response->json('data');require "net/http"
require "json"
uri = URI("https://app.mailyte.com/api/v1/email-logs")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer mk_live_YOUR_API_KEY"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }Response
Success.
dataobjectdataarray<object>objectstringeventidstringThis event's ID, the value
GET /email-logs/{event}takes.typestringemail.submitted | email.accepted | email.delivered | email.deferred | email.bounced | email.dropped | email.rejected | email.inbound | tracking.open | tracking.click | tracking.unsubscribe | delivery.complaint | spam.reported | storage.quota.warning | storage.quota.exceeded | quota.warningWhat happened, exactly as
?event_type=accepts it.email.submittedis written when we accept a message. A complaint isdelivery.complaint.quota.warningis an older name forstorage.quota.warning. Thestorage.*andspam.reportedevents are about a mailbox rather than one of your messages.recipientstringThe address the event is about. Rarely null, when the mail server's report gave no address.
detailobjectThe extras that only some event types carry. Grouped rather than flat: on a plain delivery all four are null, and flat they read as four missing fields instead of one absent category.
ip_addressstringWho opened or clicked. Null on everything else.
user_agentstringThe app that opened or clicked, as it named itself. Often null, for example when an image proxy loaded the message.
urlstringThe link that was clicked. Set only when
typeistracking.click.reasonstringWhy a message bounced, was deferred or was dropped, in the receiving server's words. Null when nothing went wrong.
not_sent_reasonstringdoes_not_exist | dead_domain | misspelt_domain | suppressed | disposable | not_a_contact | not_checked | inbox_full | nullSet when Mailyte stopped the message before it left, so it was never sent and is not a bounce: -
does_not_exist: the address is known not to exist. -dead_domain: the domain takes no mail. -misspelt_domain: the domain is misspelled, such as gmail.con. -suppressed: the address is on your suppression list. -disposable: a throwaway address. -not_a_contactornot_checked: a marketing key sends only to checked contacts. -inbox_full: the inbox is full. Remove or correct the address in your app. Null for everything else.
messageobjectThe message this event is about, without its body.
subject,fromandtagsare set only on theemail.submittedrow;GET /email-logs/{event}returns them for any event.idstringMailyte's ID for the message: a ULID, without angle brackets. Use it with
GET /email-logs/messages/{id}. It is not the Message-ID.message_idstringThe Message-ID header, angle brackets included, such as
<01JBT8XQ2M…@example.com>. It is in the recipient's headers and in any bounce. Use it, percent-encoded, withGET /domains/{domain}/messages/{messageId}. Null for anemail.droppedevent, because the message was never sent.subjectstringThe subject. In this list it is set only on the
email.submittedrow;GET /email-logs/{event}returns it for any event.fromobjecttagsarray<string>Your labels from the send.
[]means the message had no tags. Null means this row is notemail.submitted, so the tags are not shown here.metadataobjectThe metadata you set when sending, returned unchanged on every event for this message.
{}means there was none. Null means this row is notemail.submitted.
created_atstringWhen we recorded the event, which can be a little after it happened.
pagination_metaany
Returned inside the standard envelope.
Errors
| Status | When |
|---|---|
401 | The API key is missing, unknown, revoked or expired. All four answer identically, on purpose: distinguishing them would confirm which keys exist. |
403 | The key is valid but may not do this: it lacks the required scope, its IP allowlist does not include you, or this endpoint does not accept API keys. |
404 | No such resource in this organization. |
422 | The request was understood but the values were not acceptable. |
429 | Too many requests, or the organization has spent its sending allowance. `Retry-After` says how long to wait. |
Every status, with what causes it and what to do, is on the error reference.