Skip to docs content

Tweet metadata, updates, and lifecycle

Replace live metadata snapshots and merge updates by (platform, tweetId), then apply lifecycle operations without replacing stored content.

Open AsyncAPI spec
Open in chat
Reviewed against product code on September 24, 2026

tweet/meta

A live metadata payload is the latest snapshot for its post. Replace the previous metadata so omitted fields are cleared. History returns the stored enrichment attached to its row. During recovery, use it only when local metadata is empty; do not overwrite a live snapshot.

Tweet metadata type

typescript
type TweetMeta = {
  tweetId: string;
  // Pair with tweetId when merging enrichment. When omitted, use twitter.
  platform?: 'twitter' | 'truth_social' | 'binance_square';
  ocr?: {
    text: string;
  };
  detected?: {
    tokens?: Array<{
      symbol?: string;
      name?: string;
      contract?: string;
      chain?: string;
      networkId?: number;
      priceUsd?: number;
      sources: Array<'text' | 'ocr'>;
    }>;
    cex?: Array<{
      exchange: 'bybit' | 'binance' | 'hyperliquid';
      symbol?: string;
      priceUsd?: number;
      url?: string;
      baseAsset?: string;
      quoteAsset?: string;
      sources: Array<'text' | 'ocr'>;
    }>;
    prediction?: Array<{
      exchange: 'polymarket' | 'kalshi';
      marketId?: string;
      title?: string;
      priceUsd?: number;
      url?: string;
      sources: Array<'text' | 'ocr'>;
    }>;
  };
};
{
  "v": 1,
  "t": "tweet",
  "op": "meta",
  "id": "1234567890",
  "ts": 1702500001000,
  "d": {
    "tweetId": "1234567890",
    "platform": "twitter",
    "ocr": {
      "text": "Chart showing SOL breakout at $100"
    },
    "detected": {
      "tokens": [
        {
          "symbol": "SOL",
          "name": "Solana",
          "priceUsd": 98.50,
          "sources": ["text", "ocr"]
        }
      ]
    }
  }
}

tweet/update

Apply every update by (platform, tweetId). Keep absent fields; replace an included ref snapshot. Reprocess the updated post.

Tweet update type

Show full definitions (141 lines)
Tweet update typetypescript
type VerifiedType = 'blue' | 'business' | 'government' | 'verified' | 'none';
type TweetVerifiedLabel = {
  badge: string | null;
  description: string;
  url: string | null;
};
 
type TweetAuthor = {
  banner?: string;
  bio?: string;
  followersCount?: number;
  followingCount?: number;
  id?: string;
  joinedAt?: number;
  location?: string;
  metrics?: {
    likes?: number;
    tweets?: number;
  };
  // Includes a leading @ when present, for example "@elonmusk".
  handle?: string;
  name?: string;
  platform?: 'twitter' | 'truth_social' | 'binance_square';
  profileImage?: string;
  // The account's own website, when it has one.
  url?: string;
  verifiedLabel?: TweetVerifiedLabel;
  verifiedType?: VerifiedType;
};
 
type Media = {
  url: string;
} & (
  | {
      type: 'video';
      // Every video is a progressive MP4. A public still poster is included
      // when available.
      thumbnail?: string;
    }
  | {
      type?: 'image' | 'gif';
      thumbnail?: string;
    }
);
 
type TweetUrl = {
  url: string;
  name?: string;
  tco?: string;
};
 
type TweetMention = {
  handle?: string;
  id?: string;
  name?: string;
};
 
type TweetArticle = {
  description?: string;
  id?: string;
  publishedAt?: number;
  text?: string;
  thumbnail?: string;
  title: string;
  updatedAt?: number;
  url: string;
};
 
type TweetPollChoice = {
  id?: string;
  image?: string;
  label: string;
  votes?: number;
};
 
type TweetPoll = {
  choices: TweetPollChoice[];
  endsAt?: number;
  totalVotes?: number;
  updatedAt?: number;
};
 
type TweetContentKind = 'post' | 'reply' | 'quote' | 'retweet';
 
type TweetReference = {
  article?: TweetArticle;
  type: 'reply' | 'quote' | 'retweet';
  tweetId: string;
  text?: string;
  translatedText?: string;
  author?: TweetAuthor;
  media?: Media[];
  poll?: TweetPoll;
  quoted?: TweetReference;
  subtweet?: TweetReference;
};
 
type TweetContent = {
  tweetId: string;
  kind: TweetContentKind;
  // Original tweet text when the event includes both original and translated text.
  text: string;
  // Translation, present only when available.
  translatedText?: string;
  createdAt: number;
  author: TweetAuthor;
  article?: TweetArticle;
  link?: string;
  media?: Media[];
  mentions?: TweetMention[];
  poll?: TweetPoll;
  receivedAt?: number;
  urls?: TweetUrl[];
  ref?: TweetReference;
};
 
type TweetUpdate = {
  tweetId: string;
  // Pair with tweetId. When absent, use author.platform, then twitter.
  platform?: 'twitter' | 'truth_social' | 'binance_square';
  article?: TweetArticle;
  kind?: TweetContentKind;
  translatedText?: string;
  author?: TweetAuthor;
  media?: Media[];
  mentions?: TweetMention[];
  poll?: TweetPoll;
  receivedAt?: number;
  urls?: TweetUrl[];
  ref?: TweetReference;
} & (
  | {
      text?: string;
      textUpdateType?: never;
    }
  | {
      text: string;
      // Supplies the full text of a previously shortened post. Not an edit.
      textUpdateType: 'completion';
    }
);
{
  "v": 1,
  "t": "tweet",
  "op": "update",
  "id": "1234567890",
  "ts": 1702500000180,
  "d": {
    "tweetId": "1234567890",
    "platform": "twitter",
    "ref": {
      "type": "reply",
      "tweetId": "1234567880",
      "text": "Mainnet is ready for launch."
    }
  }
}
{
  "v": 1,
  "t": "tweet",
  "op": "update",
  "id": "1234567890",
  "ts": 1702500000240,
  "d": {
    "tweetId": "1234567890",
    "platform": "twitter",
    "ref": {
      "type": "reply",
      "tweetId": "1234567880",
      "text": "Mainnet is ready for launch.",
      "author": {
        "handle": "@projectteam",
        "name": "Project Team"
      },
      "subtweet": {
        "type": "quote",
        "tweetId": "1234567800",
        "text": "Launch proposal and contract details.",
        "author": {
          "handle": "@projectteam",
          "name": "Project Team"
        }
      }
    }
  }
}

Lifecycle events

Lifecycle operations update a known post and do not replace its stored content.

Tweet lifecycle types

Show full definitions (152 lines)
Tweet lifecycle typestypescript
type VerifiedType = 'blue' | 'business' | 'government' | 'verified' | 'none';
type TweetVerifiedLabel = {
  badge: string | null;
  description: string;
  url: string | null;
};
 
type TweetAuthor = {
  banner?: string;
  bio?: string;
  followersCount?: number;
  followingCount?: number;
  id?: string;
  joinedAt?: number;
  location?: string;
  metrics?: {
    likes?: number;
    tweets?: number;
  };
  // Includes a leading @ when present, for example "@elonmusk".
  handle?: string;
  name?: string;
  platform?: 'twitter' | 'truth_social' | 'binance_square';
  profileImage?: string;
  // The account's own website, when it has one.
  url?: string;
  verifiedLabel?: TweetVerifiedLabel;
  verifiedType?: VerifiedType;
};
 
type Media = {
  url: string;
} & (
  | {
      type: 'video';
      // Every video is a progressive MP4. A public still poster is included
      // when available.
      thumbnail?: string;
    }
  | {
      type?: 'image' | 'gif';
      thumbnail?: string;
    }
);
 
type TweetUrl = {
  url: string;
  name?: string;
  tco?: string;
};
 
type TweetMention = {
  handle?: string;
  id?: string;
  name?: string;
};
 
type TweetArticle = {
  description?: string;
  id?: string;
  publishedAt?: number;
  text?: string;
  thumbnail?: string;
  title: string;
  updatedAt?: number;
  url: string;
};
 
type TweetPollChoice = {
  id?: string;
  image?: string;
  label: string;
  votes?: number;
};
 
type TweetPoll = {
  choices: TweetPollChoice[];
  endsAt?: number;
  totalVotes?: number;
  updatedAt?: number;
};
 
type TweetContentKind = 'post' | 'reply' | 'quote' | 'retweet';
 
type TweetReference = {
  article?: TweetArticle;
  type: 'reply' | 'quote' | 'retweet';
  tweetId: string;
  text?: string;
  translatedText?: string;
  author?: TweetAuthor;
  media?: Media[];
  poll?: TweetPoll;
  quoted?: TweetReference;
  subtweet?: TweetReference;
};
 
type TweetContent = {
  tweetId: string;
  kind: TweetContentKind;
  // Original tweet text when the event includes both original and translated text.
  text: string;
  // Translation, present only when available.
  translatedText?: string;
  createdAt: number;
  author: TweetAuthor;
  article?: TweetArticle;
  link?: string;
  media?: Media[];
  mentions?: TweetMention[];
  poll?: TweetPoll;
  receivedAt?: number;
  urls?: TweetUrl[];
  ref?: TweetReference;
};
 
type TweetDeleteEvent = {
  tweetId: string;
  // When absent, use author.platform, then twitter.
  platform?: 'twitter' | 'truth_social' | 'binance_square';
  eventId?: string;
  deletedAt?: number;
  receivedAt?: number;
  author?: TweetAuthor;
  text?: string;
};
 
type TweetPinEvent = {
  tweetId: string;
  // When absent, use tweet.author.platform or author.platform, then twitter.
  platform?: 'twitter' | 'truth_social' | 'binance_square';
  eventId: string;
  observedAt: number;
  receivedAt?: number;
  action: 'pin';
  author: TweetAuthor;
  text?: string;
  tweet?: TweetContent;
};
 
type TweetUnpinEvent = {
  tweetId: string;
  // When absent, use tweet.author.platform or author.platform, then twitter.
  platform?: 'twitter' | 'truth_social' | 'binance_square';
  eventId: string;
  observedAt: number;
  receivedAt?: number;
  action: 'unpin';
  author: TweetAuthor;
  text?: string;
  tweet?: TweetContent;
};

tweet/delete

Mark the post deleted when this event is observed.

{
  "v": 1,
  "t": "tweet",
  "op": "delete",
  "id": "tweet-delete",
  "ts": 1772000001060,
  "d": {
    "eventId": "delete.29043b5a1a4415618c95",
    "tweetId": "tweet-delete",
    "platform": "twitter",
    "author": {
      "id": "10228272",
      "handle": "@TeamYouTube"
    },
    "deletedAt": 1772000001050,
    "receivedAt": 1772000001050,
    "text": "deleted"
  }
}

tweet/pin

Mark the post pinned and retain any included post snapshot.

{
  "v": 1,
  "t": "tweet",
  "op": "pin",
  "id": "tweet-pin",
  "ts": 1772000001060,
  "d": {
    "action": "pin",
    "author": {
      "id": "10228272",
      "handle": "@TeamYouTube"
    },
    "eventId": "pin.ab70542b9aa6bec229ef",
    "observedAt": 1772000001050,
    "tweetId": "tweet-pin",
    "platform": "twitter",
    "receivedAt": 1772000001050,
    "text": "pinned"
  }
}

tweet/unpin

Remove the pinned state for the identified post.

{
  "v": 1,
  "t": "tweet",
  "op": "unpin",
  "id": "tweet-unpin",
  "ts": 1772000001060,
  "d": {
    "action": "unpin",
    "author": {
      "id": "10228272",
      "handle": "@TeamYouTube"
    },
    "eventId": "unpin.0bef09a9f46866cd26d7",
    "observedAt": 1772000001050,
    "tweetId": "tweet-unpin",
    "platform": "twitter",
    "receivedAt": 1772000001050,
    "text": "unpinned"
  }
}