HelipodDocs

Logs and analytics

Follow each email's delivery, read project analytics and usage, and manage the suppression list.

All endpoints on this page accept an email API key (or a Helipod API key with the Email API enabled) and act on the key's project.

Email log

Every message accepted by POST /send, POST /send/batch or a broadcast, and every send the API refused before queueing, is recorded in the email log.

Email statuses

statusMeaning
queuedAccepted, waiting to be handed over for delivery.
sentHanded over for delivery.
deliveredThe recipient's mail server accepted it.
bouncedThe recipient's server refused it. The address is suppressed.
complainedThe recipient marked it as spam. The address is suppressed.
rejectedRefused, either by the API before queueing (the reason is in errorMessage) or at delivery time.
failedCould not be sent after retries. The reason is in errorMessage.

lastEvent holds the type of the most recent event, for example send, delivery, open, click, bounce.

List emails

GET/emails
Page through the email log, newest first.
QueryTypeNotes
statusstringOne of the statuses above.
searchstringSubject or recipient contains this text, or an exact messageId. Up to 200 characters.
fromstringISO date-time. Only emails created at or after it.
tostringISO date-time. Only emails created at or before it.
limitnumber1 to 200, default 50.
cursorstringnextCursor from the previous page.
curl "https://mail.helipod.io/api/email/emails?status=bounced&from=2026-10-01T00:00:00Z&limit=50" \
  -H "Authorization: Bearer your_email_api_key"
{
  "success": true,
  "data": {
    "data": [
      {
        "id": "b7e2c1d4-5a6f-4e3b-8c9d-0e1f2a3b4c5d",
        "messageId": "01a9f3c2b7d4e5f6a8b9c0d1e2f3a4b5",
        "from": "[email protected]",
        "to": ["[email protected]"],
        "subject": "Your receipt for order A-1042",
        "status": "bounced",
        "lastEvent": "bounce",
        "hasAttachments": false,
        "sentAt": "2026-10-16T09:14:04.010Z",
        "deliveredAt": null,
        "bouncedAt": "2026-10-16T09:15:12.000Z",
        "errorMessage": null,
        "createdAt": "2026-10-16T09:14:03.221Z"
      }
    ],
    "nextCursor": null
  }
}

messageId is the delivery message id. It is also what webhook events carry, so you can find an email from a webhook with ?search=<messageId>. It is null until the message has been handed over for delivery.

Errors: 400 INVALID_CURSOR.

Get one email

GET/emails/{id}
Full detail of one email, with its event timeline.
curl https://mail.helipod.io/api/email/emails/b7e2c1d4-5a6f-4e3b-8c9d-0e1f2a3b4c5d \
  -H "Authorization: Bearer your_email_api_key"
{
  "success": true,
  "data": {
    "id": "b7e2c1d4-5a6f-4e3b-8c9d-0e1f2a3b4c5d",
    "messageId": "01a9f3c2b7d4e5f6a8b9c0d1e2f3a4b5",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "cc": null,
    "bcc": null,
    "subject": "Your receipt for order A-1042",
    "replyTo": "[email protected]",
    "body": "<p>Hi Ana, thanks for order <b>A-1042</b>.</p>",
    "status": "delivered",
    "lastEvent": "open",
    "hasAttachments": false,
    "attachmentMeta": null,
    "metadata": { "orderId": "A-1042" },
    "errorMessage": null,
    "sentAt": "2026-10-16T09:14:04.010Z",
    "deliveredAt": "2026-10-16T09:14:09.880Z",
    "bouncedAt": null,
    "complainedAt": null,
    "createdAt": "2026-10-16T09:14:03.221Z",
    "events": [
      { "type": "send", "recipient": "[email protected]", "timestamp": "2026-10-16T09:14:04.010Z", "bounceType": null, "bounceSubType": null, "userAgent": null, "link": null },
      { "type": "delivery", "recipient": "[email protected]", "timestamp": "2026-10-16T09:14:09.880Z", "bounceType": null, "bounceSubType": null, "userAgent": null, "link": null },
      { "type": "open", "recipient": "[email protected]", "timestamp": "2026-10-16T09:31:40.112Z", "bounceType": null, "bounceSubType": null, "userAgent": "Mozilla/5.0", "link": null }
    ]
  }
}
  • body is the HTML (or, if there was no HTML, the text) of a send without a template, with variables already filled in. Template sends store no body: body is null and metadata carries templateId and templateName.
  • metadata is what you passed on send. For template sends, templateId, templateName and source are added and cannot be overridden.
  • events[].type is one of send, delivery, bounce, complaint, open, click, reject, failed. Bounces carry bounceType (Permanent or Transient) and bounceSubType; clicks carry link.

Returns 404 EMAIL_NOT_FOUND for an unknown id.

Get email events

GET/emails/{id}/events
Only the event timeline of one email.
curl https://mail.helipod.io/api/email/emails/b7e2c1d4-5a6f-4e3b-8c9d-0e1f2a3b4c5d/events \
  -H "Authorization: Bearer your_email_api_key"
{ "success": true, "data": { "id": "b7e2c1d4-5a6f-4e3b-8c9d-0e1f2a3b4c5d", "events": [ ] } }

Analytics

GET/analytics
Totals, rates and a daily series for a period.
QueryTypeNotes
periodtoday | 7d | 30d | 90dDefault 7d. Days are UTC. An unknown value is treated as 7d.
curl "https://mail.helipod.io/api/email/analytics?period=30d" \
  -H "Authorization: Bearer your_email_api_key"
{
  "success": true,
  "data": {
    "period": "30d",
    "start": "2026-09-17",
    "totals": {
      "sent": 1200, "delivered": 1180, "bounced": 12, "hardBounced": 8, "softBounced": 4,
      "complained": 1, "rejected": 3, "opened": 610, "clicked": 140,
      "received": 0, "receivedBytes": 0
    },
    "rates": {
      "deliveryRate": "98.33", "bounceRate": "1.00", "hardBounceRate": "0.67",
      "complaintRate": "0.08", "openRate": "51.69", "clickRate": "11.86"
    },
    "limits": { "bounce": 0.02, "complaint": 0.0008 },
    "receiving": null,
    "series": [
      { "date": "2026-10-16", "sent": 40, "delivered": 39, "bounced": 1, "hardBounced": 1, "complained": 0, "opened": 20, "clicked": 4, "received": 0, "receivedBytes": 0 }
    ]
  }
}
  • Sending numbers are grouped by the day the email was sent: a bounce or open that arrives the next day still counts toward the send day.
  • rates are percentages as strings with two decimals. complaintRate, openRate and clickRate are relative to delivered emails (or sent, when nothing was delivered).
  • limits are the bounce and complaint rates (as fractions) above which sending for the project is suspended. See Reputation.
  • receiving is present when the plan includes an inbox: { cycleStart, cycleReceivedBytes, cycleLimitBytes }.

Project status

GET/status
Activation, plan, usage and counts for the project in one call.
curl https://mail.helipod.io/api/email/status \
  -H "Authorization: Bearer your_email_api_key"

Abridged response:

{
  "success": true,
  "data": {
    "activated": true,
    "configSet": {
      "sendingEnabled": true,
      "status": "active",
      "autoDisabled": false,
      "autoDisabledReason": null,
      "dailySendCount": 40,
      "dailySendLimit": 0,
      "bounceRate": "0.0042",
      "complaintRate": "0.0000"
    },
    "platformPaused": null,
    "plan": { "code": "STARTER", "monthlySendLimit": 50000, "dailySendLimit": 0 },
    "subscription": { "status": "active", "expiredAt": "2026-11-01T00:00:00.000Z", "autoRenew": true, "paygEnabled": false },
    "usage": { "today": 40, "month": 1200, "dailyLimit": 0, "monthlyLimit": 50000, "dailyRemaining": 0 },
    "domains": { "total": 1, "verified": 1 },
    "keys": { "total": 2 },
    "contacts": { "total": 3400, "subscribed": 3310 },
    "broadcasts": { "allowBroadcasts": true, "active": 0 }
  }
}

Useful fields:

  • usage: emails counted today (UTC) and in the current monthly window, with the limits. A limit of 0 means no limit of that kind on your plan (dailyRemaining is then 0 too and can be ignored).
  • configSet.autoDisabled and autoDisabledReason: the project is suspended for its bounce or complaint rate.
  • configSet.bounceRate and complaintRate: the rolling rates as fractions.
  • platformPaused: a reason string when sending is paused for every project on Helipod Email, otherwise null.
  • plan: the active plan's limits and feature flags (for example maxDomains, maxTemplates, maxContacts, allowBroadcasts), or null without a plan.
  • subscription: the latest subscription, including whether pay-as-you-go (paygEnabled) is on.

Suppression list

The suppression list holds addresses this project will not mail. Entries are added automatically on a hard bounce (permanent), a soft bounce (30 days), a spam complaint (permanent) and an unsubscribe (permanent), and you can add your own.

reasonBlocks transactional sendsBlocks broadcastsExpires
hard_bounceyesyesnever
complaintyesyesnever
soft_bounceyesyesafter 30 days
unsubscribednoyesnever

In addition, an address that really hard-bounced or complained in any Helipod Email project cannot receive transactional sends from yours either; the error says only that the address previously bounced or complained. Entries you add yourself apply to your project only.

List suppressions

GET/suppressions
Most recent entries first.
QueryTypeNotes
limitnumber1 to 500, default 100. Not paginated.
curl "https://mail.helipod.io/api/email/suppressions?limit=200" \
  -H "Authorization: Bearer your_email_api_key"
{
  "success": true,
  "data": [
    {
      "id": "7c6b5a4f-3e2d-4c1b-8a09-f8e7d6c5b4a3",
      "email": "[email protected]",
      "reason": "hard_bounce",
      "source": "bounce",
      "suppressedAt": "2026-10-10T03:12:44.000Z",
      "expiresAt": null,
      "permanent": true
    }
  ]
}

source tells where the entry came from, for example bounce, complaint, unsubscribe, unsubscribe_link or manual.

Add a suppression

POST/suppressions
Stop mailing an address. Adding an existing address updates its reason.

Prop

Type

curl -X POST https://mail.helipod.io/api/email/suppressions \
  -H "Authorization: Bearer your_email_api_key" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","reason":"unsubscribed"}'
{ "success": true, "data": { "id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d", "email": "[email protected]", "reason": "unsubscribed" } }

Response status is 201.

Remove a suppression

DELETE/suppressions/{id}
Allow mailing an address again.
curl -X DELETE https://mail.helipod.io/api/email/suppressions/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d \
  -H "Authorization: Bearer your_email_api_key"
{ "success": true, "data": { "deleted": true } }

Only remove an entry when you are sure the address works again and the person wants your mail. Returns 404 SUPPRESSION_NOT_FOUND for an unknown id.

On this page