Skip to main content

Email lifecycle

Every email goes through a series of statuses from the moment you send it:
queued → sending → sent → delivered
                       ↘ bounced
                       ↘ failed
If you set scheduledAt, the email starts in scheduled until the delivery time:
scheduled → queued → sending → sent → delivered
StatusDescription
scheduledEmail is scheduled for future delivery
queuedEmail accepted and queued for processing
sendingEmail is being sent to the mail provider
sentEmail handed off to the mail provider
deliveredEmail accepted by the recipient’s mail server
bouncedEmail rejected by the recipient’s mail server
failedEmail failed to send (provider error, content issue, etc.)

Event types

Each email generates events as it moves through the delivery pipeline:
EventDescription
queuedEmail accepted into the queue
sendingEmail picked up for delivery
sentHanded off to mail provider
deliveredSuccessfully delivered to recipient’s inbox
bouncedEmail bounced (generic bounce event)
hard_bounceHard bounce — permanent delivery failure
soft_bounceSoft bounce — temporary issue (mailbox full, server down)
deferredDelivery deferred — will be retried
openedRecipient opened the email
clickedRecipient clicked a link in the email
unsubscribedRecipient unsubscribed
spam_reportRecipient marked the email as spam
blockedEmail blocked by recipient’s server
failedPermanent send failure
cancelledScheduled email was cancelled before delivery

Get events for an email

Retrieve the full event timeline for a specific email:
curl https://api.mail.gorillaa.one/v1/emails/em_abc123/events \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

{
  "data": [
    {
      "type": "queued",
      "timestamp": "2026-02-09T10:00:00Z"
    },
    {
      "type": "sent",
      "timestamp": "2026-02-09T10:00:02Z"
    },
    {
      "type": "delivered",
      "timestamp": "2026-02-09T10:00:05Z"
    },
    {
      "type": "opened",
      "timestamp": "2026-02-09T10:15:30Z",
      "metadata": {
        "userAgent": "Mozilla/5.0...",
        "ipAddress": "203.0.113.42"
      }
    },
    {
      "type": "clicked",
      "timestamp": "2026-02-09T10:16:12Z",
      "metadata": {
        "url": "https://yourdomain.com/dashboard",
        "userAgent": "Mozilla/5.0..."
      }
    }
  ]
}

Get email details with counts

The email detail response includes delivery status and the full event timeline:
{
  "data": {
    "id": "em_abc123",
    "organizationId": "org_xyz",
    "status": "delivered",
    "fromEmail": "[email protected]",
    "fromName": "Your App",
    "toEmails": ["[email protected]"],
    "ccEmails": [],
    "bccEmails": [],
    "subject": "Your invoice is ready",
    "createdAt": "2026-02-09T10:00:00Z",
    "sentAt": "2026-02-09T10:00:02Z",
    "deliveredAt": "2026-02-09T10:00:05Z",
    "events": [
      { "type": "queued", "timestamp": "2026-02-09T10:00:00Z" },
      { "type": "sent", "timestamp": "2026-02-09T10:00:02Z" },
      { "type": "delivered", "timestamp": "2026-02-09T10:00:05Z" },
      { "type": "opened", "timestamp": "2026-02-09T10:15:30Z" }
    ]
  }
}

Side effects

Some events automatically trigger actions in your account:
EventSide effect
hard_bounceRecipient added to suppression list
spam_reportRecipient added to suppression list
unsubscribedRecipient added to suppression list
deliveredEmail status updated to delivered
openedopenCount incremented
clickedclickCount incremented
Suppressed addresses are automatically skipped on future sends, protecting your sender reputation. See Suppressions for details.

Polling for events

To track an email’s progress, poll the events endpoint:
async function waitForDelivery(emailId, apiKey) {
  const maxAttempts = 10;
  const delayMs = 5000; // 5 seconds

  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(
      `https://api.mail.gorillaa.one/v1/emails/${emailId}`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    );
    const { data: email } = await response.json();

    if (["delivered", "bounced", "failed"].includes(email.status)) {
      return email;
    }

    await new Promise((r) => setTimeout(r, delayMs));
  }

  throw new Error("Timed out waiting for delivery");
}
For high-volume use cases, use the Analytics endpoints instead of polling individual emails.