> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tweet-catcher.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Events

> JSON shapes pushed over WebSocket and posted to custom webhooks

Every notification uses the same envelope. Over the [WebSocket](https://docs.tweet-catcher.com/pro/websocket.md) it arrives inside an `op: 0` message as `d`. A custom webhook receives the same object as the POST body

```json theme={null}
{
  "op": 0,
  "d": {
    "event_id": "a1b2c3d4",
    "event": "twitter.post.new",
    "task_info": { "id": 1042, "group": "markets", "handle": "elonmusk", "profile_info": {} },
    "data": {}
  }
}
```

## Envelope

**event\_id** `string`

Stable for a given post, so you can use it to drop duplicates. Not sent on keyword events

**event** `string`

The event type. See [Event types](#event-types)

**task\_info** `object`

The task that produced the event

**properties**

**id** `integer`

Task ID. Present when the task has one

**group** `string`

Task group. Present when the task has one

**handle** `string`

Monitored handle. Account tasks only

**profile\_info** `object`

[Profile](#profile) of the monitored account. Account tasks only

**keyword** `string`

Monitored query. Keyword tasks only

**data** `object`

The event payload. Its shape depends on `event`. Absent on `twitter.task.update`

**status** `string`

Only on `twitter.task.update`. One of `profile_private`, `profile_suspended`, `profile_available_back`

## Event types

| `event` | Sent when | `data` |
| - | - | - |
| `twitter.post.new`, `truth_social.post.new` | The account posts, reposts, quotes, or replies | [Post](#post) |
| `twitter.post.micro` | A post is detected, before the full `twitter.post.new` | Partial [post](#post) |
| `twitter.post.subscription` | The account publishes a subscriber-only post | [Post](#post) |
| `twitter.post.private` | A protected account posts | `{ "statuses_count": 1200 }` |
| `twitter.post.deleted` | The account deletes a post | [Post](#post) |
| `twitter.post.ocr`, `truth_social.post.ocr` | Text has been read from a posted image | [OCR result](#ocr) |
| `twitter.post.translate`, `truth_social.post.translate` | A post translation is ready | [Translation](#translation) |
| `twitter.following.new` | The account follows someone | [Profile](#profile) of the followed account |
| `twitter.following.removed` | The account unfollows someone | [Profile](#profile) of the unfollowed account |
| `twitter.profile.update` | The account changes its profile | [Changed fields](#profile-updates) |
| `twitter.task.update` | The account goes private, is suspended, or comes back | None, see `status` |
| `twitter_keyword.post.new` | A post matches a Twitter Global query | [Keyword post](#keyword-posts) |

## Account events

### New posts

Sent for every post type enabled on the task. Check `data.type` to tell posts, reposts, quotes, and replies apart

```json theme={null}
{
  "event_id": "a1b2c3d4",
  "event": "twitter.post.new",
  "task_info": { "id": 1042, "group": "markets", "handle": "elonmusk", "profile_info": {} },
  "data": {
    "id": "123",
    "created_at": 1730000000000,
    "type": "quote",
    "text": "Interesting",
    "media": [],
    "mentions": [],
    "tags": [],
    "author": { "id": "44196397", "handle": "elonmusk", "name": "Elon Musk" },
    "quoted": {
      "id": "122",
      "created_at": 1729990000000,
      "type": "post",
      "text": "Original post",
      "media": [{ "type": "photo", "url": "https://pbs.twimg.com/..." }],
      "mentions": [],
      "tags": [],
      "author": { "id": "12", "handle": "jack", "name": "jack" }
    }
  }
}
```

> [!NOTE]
>
> `twitter.post.micro` is a Pro feature and is only sent while micro events are enabled. It carries a partial post so you can react faster, followed by the complete `twitter.post.new` for the same post

### Subscriber and protected posts

`twitter.post.subscription` carries a full [post](#post) with `type` set to `subscription`

`twitter.post.private` is sent when a protected account posts. The content is not visible, so `data` only holds the account's new post count:

```json theme={null}
{
  "event_id": "a1b2c3d4",
  "event": "twitter.post.private",
  "task_info": { "id": 1042, "handle": "someprivateuser", "profile_info": {} },
  "data": { "statuses_count": 1200 }
}
```

### Deleted posts

`twitter.post.deleted` carries the deleted [post](#post) as it was last seen

### OCR

Sent when text has been read from an image in a post. Requires the `ocr` option on the task

```json theme={null}
{
  "event_id": "a1b2c3d4",
  "event": "twitter.post.ocr",
  "task_info": { "id": 1042, "handle": "elonmusk", "profile_info": {} },
  "data": {
    "text": "read from the image",
    "image_url": "https://pbs.twimg.com/...",
    "post_id": "123"
  }
}
```

Match `post_id` against the `id` of an earlier post event to link the result to its post.

### Translation

Sent when a post translation is ready. `text` has one entry for the post and one for each nested post (quoted, reposted, or replied) that was translated

```json theme={null}
{
  "event_id": "a1b2c3d4",
  "event": "twitter.post.translate",
  "task_info": { "id": 1042, "handle": "elonmusk", "profile_info": {} },
  "data": {
    "post_id": "123",
    "text": [{ "original": "Bonjour", "translatedText": "Hello" }]
  }
}
```

### Following and unfollowing

`twitter.following.new` and `twitter.following.removed` carry the [profile](#profile) of the account that was followed or unfollowed. The task's following filters apply before the event is sent

```json theme={null}
{
  "event_id": "a1b2c3d4",
  "event": "twitter.following.new",
  "task_info": { "id": 1042, "handle": "elonmusk", "profile_info": {} },
  "data": {
    "id": "12",
    "created_at": 1142974214000,
    "handle": "jack",
    "name": "jack",
    "avatar": "https://pbs.twimg.com/...",
    "description": "",
    "stats": { "followers": 6000000, "following": 4000, "posts": 30000, "media": 2000, "likes": 35000 }
  }
}
```

### Profile updates

Only the fields that changed are present in `data`. Each one holds the previous and new value

```json theme={null}
{
  "event_id": "a1b2c3d4",
  "event": "twitter.profile.update",
  "task_info": { "id": 1042, "handle": "elonmusk", "profile_info": {} },
  "data": {
    "description": { "prev": "old bio", "updated": "new bio" },
    "verification": { "prev": "", "updated": "Business" }
  }
}
```

**\<field>** `object`

One key per changed field: `handle`, `name`, `avatar`, `banner`, `location`, `description`, `website`, `pinned_post`, `verification`

**properties**

**prev** `string`

Value before the change.

**updated** `string`

Value after the change.

**post** `object`

Only on `pinned_post`. The new pinned [post](#post)

Fields turned off in the task's profile update options are never reported

### Task status

Sent when the monitored account becomes unavailable or available again. There is no `data`. Read `status` instead

```json theme={null}
{
  "event_id": "a1b2c3d4",
  "event": "twitter.task.update",
  "task_info": { "id": 1042, "handle": "elonmusk", "profile_info": {} },
  "status": "profile_suspended"
}
```

| `status` | Meaning |
| - | - |
| `profile_private` | The account became protected |
| `profile_suspended` | The account was suspended |
| `profile_available_back` | The account is public again |

## Keyword posts

[Twitter Global](https://docs.tweet-catcher.com/plans.md#twitter-global) tasks send `twitter_keyword.post.new`. These events have no `event_id`, and `task_info` has `keyword` instead of `handle` and `profile_info`. The post has no `type` or `mentions`, and `author` is a lighter profile

```json theme={null}
{
  "event": "twitter_keyword.post.new",
  "task_info": { "id": 1045, "group": "markets", "keyword": "(bitcoin OR ethereum) NOT (giveaway)" },
  "data": {
    "id": "123",
    "created_at": 1730000000000,
    "text": "text",
    "media": [],
    "tags": ["bitcoin"],
    "author": {
      "id": "12",
      "created_at": 1200000000000,
      "handle": "jack",
      "name": "jack",
      "avatar": "https://pbs.twimg.com/...",
      "verification": { "is_verified": true, "type": "Business" },
      "affiliation": { "label": "Square", "badge": "https://...", "type": "BusinessLabel" }
    }
  }
}
```

`verification.type` is `Business` or `Government` when present. `affiliation` is only present when the account has one

## Objects

### Post

**id** `string`

Post ID

**created\_at** `integer`

Post time in milliseconds

**type** `string`

`post`, `repost`, `quote`, `reply`, or `subscription`

**text** `string`

Post text

**media** `object[]`

Photos and videos in the post

**properties**

**type** `string`

`photo` or `video`

**url** `string`

URL of the photo or video

**preview** `string`

Videos only. URL of the first frame

**duration** `integer`

Videos only. Length in milliseconds

**url\_original** `string`

Truth Social photos only. Full-size image

**mentions** `object[]`

Accounts mentioned in the post

**properties**

**id** `string`

Account ID of the mentioned profile

**handle** `string`

Username of the mentioned profile, without the `@`

**name** `string`

Display name of the mentioned profile

**tags** `string[]`

Hashtags without the `#`

**author** `object`

[Profile](#profile) of the author

**quoted** `object`

Quotes only. The quoted post, with the same shape

**reposted** `object`

Reposts only. The original post, with the same shape

**replied** `object`

Replies only. The post being replied to, with the same shape

### Profile

```json theme={null}
{
  "id": "44196397",
  "created_at": 1243979740000,
  "handle": "elonmusk",
  "name": "Elon Musk",
  "avatar": "https://pbs.twimg.com/...",
  "banner": "https://pbs.twimg.com/...",
  "description": "",
  "location": "",
  "website": "",
  "verification": { "is_verified": true, "type": "Business" },
  "affiliation": { "label": "Tesla", "badge": "https://...", "type": "BusinessLabel" },
  "stats": { "followers": 1, "following": 1, "posts": 1, "media": 1, "likes": 1 }
}
```

**id** `string`

Account ID. Unlike `handle`, it never changes

**created\_at** `integer`

Account creation time in milliseconds

**handle** `string`

Username without the `@`

**name** `string`

Display name

**avatar** `string`

Profile picture URL. Truth Social can also include `avatar_original`

**banner** `string`

Banner URL

**description** `string`

Bio

**location** `string`

Location set on the profile

**website** `string`

Website set on the profile

**verification** `object`

Verification badge

**properties**

**is\_verified** `boolean`

Whether the profile has a verified badge

**type** `string`

Badge type, such as `Business` or `Government`. Can be omitted

**affiliation** `object`

Affiliate label shown next to the name

**properties**

**label** `string`

Name of the affiliated organization

**badge** `string`

URL of the badge image

**type** `string`

Label type, such as `BusinessLabel`

**stats** `object`

Profile counters

**properties**

**followers** `integer`

Number of followers

**following** `integer`

Number of accounts the profile follows

**posts** `integer`

Number of posts

**media** `integer`

Number of posts with media

**likes** `integer`

Number of posts the profile has liked

`verification`, `affiliation`, and individual stats are omitted when Twitter does not return them


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.