Skip to content
YourMail

API reference

List emails

GET/v1/emails

Page through every email you've sent, newest first, optionally filtered by status.

Why use this

Retrieve answers “what happened to this email?”. List answers “what happened to all of them?” — it's how you reconcile sending against your own records, build internal reporting, or sweep up failures in a scheduled job.

For example

Every morning you pull yesterday's bounced emails and flag those users' addresses in your own database, so your support team knows who never got their receipt.

Example

import { YourMail } from "yourmail";

const yourmail = new YourMail(process.env.YOURMAIL_API_KEY!);

const page = await yourmail.list({ limit: 50 });

for (const email of page.data) {
  console.log(email.id, email.status, email.subject);
}

Query parameters

All parameters are optional.

limitnumber
Page size, 1–100. Defaults to 50. Values outside the range are clamped rather than rejected.
cursorstring
Opaque cursor taken from a previous response's nextCursor. Omit it to start from the newest email.
statusstring
Optional filter. One of scheduled, queued, sent, delivered, bounced, complained, failed, canceled. Any other value is a 400. Note that queued also covers a message that is mid-dispatch.
searchstring
Optional full-text term, matched against recipients, sender, subject and tag values — the same index the dashboard searches. Combines with status, with one caveat: a search is served by the search index, where status is matched exactly, so search plus status=queued does not also return mid-dispatch messages the way status=queued alone does. Results come back in relevance order rather than newest-first for the same reason; paging works the same way.

Response

// 200 OK
{
  "data": [
    {
      "id": "jn7abc123def456",
      "to": ["alice@example.com"],
      "from": "hello@mail.acme.com",
      "subject": "Welcome to Acme",
      "status": "delivered",
      "tags": [{ "name": "campaign", "value": "welcome" }],
      "createdAt": 1750000000000,
      "sentAt": 1750000001234,
      "openedAt": 1750000090000,
      "clickedAt": null
    }
  ],
  "hasMore": true,
  "nextCursor": "eyJpZCI6..."
}
dataobject[]
The page of emails, newest first.
data[].idstring
Unique email identifier — pass it to the retrieve endpoint for the full record.
data[].tostring[]
Recipient addresses.
data[].fromstring
Sender address as supplied in the send request.
data[].subjectstring
Email subject line.
data[].statusstring
Current delivery status.
data[].tags{name,value}[]
Tags attached at send time.
data[].createdAtnumber
Unix millisecond timestamp when the email was created.
data[].sentAtnumber | null
Unix ms when the message was accepted for delivery. Null until the status reaches sent.
data[].scheduledAtnumber | null
Unix ms a scheduled send fires at. Null for a message sent immediately — so status=scheduled answers when without a request per row.
data[].openedAtnumber | null
Unix ms of the first recorded open. Null if never opened.
data[].clickedAtnumber | null
Unix ms of the first recorded click. Null if no link was clicked.
hasMoreboolean
True when another page is available. A page can legitimately come back EMPTY with hasMore true — a status filter that skips a long run of rows returns what it found in a bounded window rather than reading indefinitely. Iterate until hasMore is false; don't stop on an empty page.
nextCursorstring | null
Pass as cursor to fetch the next page. Null on the last page.

Pagination

Results are cursor-paginated. Read nextCursor from a response and pass it back as cursor to get the following page; when it comes back null, you've reached the end. Cursors are opaque — don't construct or modify them.

// Walk every page. `nextCursor` is null on the last one.
let cursor: string | undefined;

do {
  const page = await yourmail.list({ limit: 100, cursor });
  for (const email of page.data) {
    // ...
  }
  cursor = page.nextCursor ?? undefined;
} while (cursor);

Errors

400validation_error

The status filter isn't a recognised status, the cursor isn't a valid pagination cursor, or search is over 512 characters or 16 terms

401authentication_error

API key missing, malformed, or not recognised

403authentication_error

The key is send-only; listing requires a full-access key

500internal_error

This page couldn't be assembled completely — rare, and specific to very large messages. A smaller limit does not help; retry, or narrow with status or search