---
title: "Activity on a legacy email campaign"
method: POST
path: "emailCampaignActivity"
tags: ["Webhooks"]
---

# Activity on a legacy email campaign

`POST emailCampaignActivity` (webhook)

Sent to URLs registered with `type: email` (or without `type`).

- `SENT`: right after a sequence email is sent (test sends excluded). Network errors retried up to 5 times; the send waits for your endpoint.
- `OPENED`: tracking pixel loaded, at most once per email and contact, delivered 30 to 60 seconds later. Needs open tracking.
- `CLICKED`: tracked link clicked, at most once per link and email, delivered 30 to 60 seconds later; two clicks of the same contact on the same step queued within that delay can be merged into the last one. With open tracking on, a click is ignored until an open was recorded.
- `REPLIED`: first reply of the contact detected in the mailbox (later replies are not sent), with reply text, AI classification, sending mailbox and Message-ID. Network errors retried up to 5 times.
- `BOUNCED`: bounce detected (non delivery report or refusal at send time). No `campaign` field and `step` always 0. Network errors retried up to 5 times.
- `UNSUBSCRIBED`: the contact confirms on the unsubscribe page (or a one click unsubscribe POST is received); once per contact.
- `INTERESTED`: contact marked as interested by a user in the legacy web app (not through the REST API).
- `CAMPAIGN_ENDING`: accepted at registration, never sent for email campaigns.

**Delivery.** One `POST` per event and per registered URL, header `Content-Type: application/json`, JSON body, no signature or secret header, default User-Agent of the HTTP library. No timeout is set. Your response (status and body) is ignored: an HTTP error is never retried. Network errors are retried only for some events (see each list). No event ID, no ordering guarantee, no replay; a URL registered twice receives everything twice. Absent fields are omitted, never null.

## Payload

- WebhookEmailActivityPayload
  - `event` 'SENT' | 'OPENED' | 'CLICKED' | 'REPLIED' | 'BOUNCED' | 'UNSUBSCRIBED' | 'INTERESTED' | 'CAMPAIGN_ENDING', required: Events delivered for legacy email campaigns (`type: email`). `CAMPAIGN_ENDING` is accepted but never delivered for email campaigns in the current code.
  - `campaign` string: Campaign name. Missing on BOUNCED (bug).
  - `campaignId` string: REPLIED only.
  - `sender` string: Address of the Emelia sending mailbox (the one that received the reply). REPLIED, OPENED and CLICKED only.
  - `senderName` string: REPLIED only.
  - `contact` WebhookEmailContact, required: Contact of a legacy email campaign. Starts with every custom field of the contact (any key), then the standard fields, which win on name clashes. `company` is the value of the first custom field whose name contains `company` (case insensitive), absent otherwise. No contact ID.
    - `firstName` string
    - `lastName` string
    - `phoneNumber` string
    - `email` string, email
    - `company` string
  - `date` string, date-time, required: ISO 8601 UTC. Time at which Emelia processed the event: send time, detection time for replies (not the reply's own date for email), processing time 30 to 60 seconds after the open or click for OPENED and CLICKED.
  - `step` integer, required: 0 based index of the sequence step. Always 0 for BOUNCED and INTERESTED. Can be -1 when the step cannot be resolved.
  - `reply` string: REPLIED only. Plain text of the reply (HTML when no text part), usually including the quoted thread.
  - `sentiment` WebhookSentiment: AI classification of a reply. Present only when reply classification is enabled on the account and AI credits remain. Classified by Jev, with Gemini as fallback; only the Gemini fallback can add `additionalData`.
    - `classification` 'INTERESTED' | 'NOT_INTERESTED' | 'NEUTRAL' | 'OUT_OF_OFFICE' | 'HOSTILE' | 'DELEGATION' | 'INCOMPREHENSION' | 'QUESTION_ASK'
    - `score` number: Confidence of the classification.
    - `additionalData` object: Gemini fallback only: `delegationEmail` for DELEGATION, `returnDate` (DD/MM/YYYY) for OUT_OF_OFFICE.
      - `delegationEmail` string
      - `returnDate` string
  - `messageId` string: REPLIED only. Message-ID of the reply, usable to answer in the same thread.
  - `link` string: CLICKED only. Original URL of the clicked link.

## Acknowledgement `2XX`

Any response is accepted. Emelia ignores the status and the body.

## Other responses

- `default`: Ignored too: errors are not retried and the webhook stays active.

---

[API](https://skmtc.dev/emelia/apis/emelia-api.md) · [All operations](https://skmtc.dev/emelia/apis/emelia-api/llms.txt) · [OpenAPI document](https://skmtc.dev/emelia/apis/emelia-api/revisions/8c0cb435f2c4?raw)
