# Activity on a legacy LinkedIn campaign

Source: https://docs.emelia.io/docs/webhooks/legacy-linkedin-activity

Guide: [Webhooks](https://docs.emelia.io/docs/webhooks.md)

Webhook event `linkedinCampaignActivity`: Emelia sends an HTTP POST request to your URL.

Sent to URLs registered with `type: linkedin` or `linkedin: true`.

- `VISITED`: profile visited.
- `INVITED`: invitation sent. When the contact was already a connection, `INVITED` and `ACCEPTED` are sent together.
- `ACCEPTED`: acceptance detected (connections polling, inferred from a reply, or real time LinkedIn notification).
- `MESSAGE_SENT`: message sent, text in `content`.
- `INMAIL_SENT`: InMail sent.
- `REPLIED`: first reply detected, with `reply` and `sentiment`.

`LIKED`, `FOLLOWED` and `CAMPAIGN_ENDING` are never sent for these campaigns. Most events come from the LinkedIn worker (contact without `id` nor `email`). Replies and acceptances detected in real time come from the API server and add `campaignId`, `contact.id` and `contact.email`; that path retries once on network errors, the worker never retries.

**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

| Field | Type | Required | Description |
|---|---|---|---|
| `event` | enum: `VISITED`, `INVITED`, `ACCEPTED`, `MESSAGE_SENT`, `INMAIL_SENT`, `REPLIED` | yes | Events delivered for legacy LinkedIn campaigns (`type: linkedin`). `LIKED`, `FOLLOWED` and `CAMPAIGN_ENDING` are never delivered for these campaigns. |
| `campaign` | string | yes | Campaign name. |
| `campaignId` | string | no | Only on replies and acceptances detected in real time. |
| `contact` | object | yes | Contact of a legacy LinkedIn campaign: custom fields first, then the standard fields. `id` (Emelia contact ID) and `email` are only present when the event was detected in real time by the API server (replies and acceptances); events from the LinkedIn worker never have them. |
| `contact.id` | string | no | Real time replies and acceptances only. |
| `contact.firstName` | string | no |  |
| `contact.lastName` | string | no |  |
| `contact.email` | string | no | Real time replies and acceptances only. |
| `contact.linkedinUrlProfile` | string (uri) | no |  |
| `contact.linkedinId` | string | no | LinkedIn member ID. |
| `contact.company` | string | no |  |
| `date` | string (date-time) | yes | ISO 8601 UTC. Action time, or message time for replies. |
| `step` | integer | yes | 0 based index of the sequence step (-1 when not resolved). |
| `reply` | string | no | REPLIED only. |
| `sentiment` | object | no | 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`. |
| `sentiment.classification` | enum: `INTERESTED`, `NOT_INTERESTED`, `NEUTRAL`, `OUT_OF_OFFICE`, `HOSTILE`, `DELEGATION`, `INCOMPREHENSION`, `QUESTION_ASK` | no |  |
| `sentiment.score` | number | no | Confidence of the classification. Range: 0 to 1. |
| `sentiment.additionalData` | object | no | Gemini fallback only: `delegationEmail` for DELEGATION, `returnDate` (DD/MM/YYYY) for OUT_OF_OFFICE. |
| `sentiment.additionalData.delegationEmail` | string | no |  |
| `sentiment.additionalData.returnDate` | string | no |  |
| `content` | string | no | MESSAGE_SENT only: text of the message sent. |

**VISITED**

```json
{
  "event": "VISITED",
  "campaign": "Acme LinkedIn Q4",
  "contact": {
    "companyName": "Acme",
    "firstName": "John",
    "lastName": "Doe",
    "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
    "company": "Acme",
    "linkedinId": "ACoAAB1x2y3zAcmeExample"
  },
  "date": "2026-10-12T08:30:00.000Z",
  "step": 0
}
```

**MESSAGE_SENT (with content)**

```json
{
  "event": "MESSAGE_SENT",
  "campaign": "Acme LinkedIn Q4",
  "contact": {
    "companyName": "Acme",
    "firstName": "John",
    "lastName": "Doe",
    "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
    "company": "Acme",
    "linkedinId": "ACoAAB1x2y3zAcmeExample"
  },
  "date": "2026-10-14T08:30:00.000Z",
  "step": 2,
  "content": "Hi John, thanks for connecting. Quick question about Acme..."
}
```

**REPLIED from the LinkedIn worker**

```json
{
  "event": "REPLIED",
  "campaign": "Acme LinkedIn Q4",
  "contact": {
    "companyName": "Acme",
    "firstName": "John",
    "lastName": "Doe",
    "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
    "company": "Acme",
    "linkedinId": "ACoAAB1x2y3zAcmeExample"
  },
  "date": "2026-10-15T07:45:12.000Z",
  "step": 2,
  "reply": "Hi, happy to chat next week.",
  "sentiment": {
    "classification": "INTERESTED",
    "score": 0.92
  }
}
```

**REPLIED detected in real time (API server)**

```json
{
  "event": "REPLIED",
  "campaign": "Acme LinkedIn Q4",
  "campaignId": "6650aa10b2c3d4e5f6071829",
  "contact": {
    "companyName": "Acme",
    "id": "6703d2e5f6a7b8c9d0e1f2a3",
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@acme.com",
    "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
    "linkedinId": "ACoAAB1x2y3zAcmeExample",
    "company": "Acme"
  },
  "date": "2026-10-15T07:45:12.000Z",
  "step": 2,
  "reply": "Hi, happy to chat next week.",
  "sentiment": {
    "classification": "INTERESTED",
    "score": 0.92
  }
}
```
