Guides

AWS SNS Documentation

Introduction

As a developer working with the Lens protocol, you can subscribe to a stream of events happening in real time via AWS SNS. You do not need an AWS account in order to subscribe to events.

Usage & Access

Events are published via http requests. To get access to the stream of events, please contact someone from Lens and tell them your http endpoint which will receive POST requests for incoming events. In order for events to be sent to your endpoint, you must make a GET request to the SubscribeURL mentioned in the SubscriptionConfirmation message type (example below). If you do not, you will not receive any events to your endpoint.

How to listen to the events

This example is given in node and express you can of course use a different language or framework to do this.

import bodyParser from 'body-parser';
import express from 'express';
import fetch from 'node-fetch';

const app = express();
const port = 8080;

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.post('/lens/notifications', async (req, res) => {
  const buffers = [];

  for await (const chunk of req) {
    buffers.push(chunk);
  }

  const data = Buffer.concat(buffers).toString();
  // example https://docs.aws.amazon.com/connect/latest/adminguide/sns-payload.html
  const payload = JSON.parse(data);

  // if you already done the handshake you will get a Notification type
  // example below: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html
  // {
  //   "Type" : "Notification",
  //   "MessageId" : "22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324",
  //   "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
  //   "Subject" : "My First Message",
  //   "Message" : "Hello world!",
  //   "Timestamp" : "2012-05-02T00:54:06.655Z",
  //   "SignatureVersion" : "1",
  //   "Signature" : "EXAMPLEw6JRN...",
  //   "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
  //   "UnsubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96"
  // }
  if (payload.Type === 'Notification') {
    console.log('sns message is a notification ', payload);
    console.log('------------------------------------------------------');
    console.log('------------------------------------------------------');
    console.log('------------------------------------------------------');
    res.sendStatus(200);
    return;
  }

  // only need to do this the first time this is doing an handshake with the sns client
  // example below: https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html
  // {
  //   "Type" : "SubscriptionConfirmation",
  //   "MessageId" : "165545c9-2a5c-472c-8df2-7ff2be2b3b1b",
  //   "Token" : "2336412f37...",
  //   "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
  //   "Message" : "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:123456789012:MyTopic.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
  //   "SubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:123456789012:MyTopic&Token=2336412f37...",
  //   "Timestamp" : "2012-04-26T20:45:04.751Z",
  //   "SignatureVersion" : "1",
  //   "Signature" : "EXAMPLEpH+DcEwjAPg8O9mY8dReBSwksfg2S7WKQcikcNKWLQjwu6A4VbeS0QHVCkhRS7fUQvi2egU3N858fiTDN6bkkOxYDVrY0Ad8L10Hs3zH81mtnPk5uvvolIC1CXGu43obcgFxeL3khZl8IKvO61GWB6jI9b5+gLPoBc1Q=",
  //   "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem"
  // }
  if (payload.Type === 'SubscriptionConfirmation') {
    const url = payload.SubscribeURL;
    const response = await fetch(url);
    if (response.status === 200) {
      console.log('Subscription confirmed');
      console.log('------------------------------------------------------');
      console.log('------------------------------------------------------');
      console.log('------------------------------------------------------');
      res.sendStatus(200);
      return;
    } else {
      console.error('Subscription failed');
      res.sendStatus(500);
      return;
    }
  }

  console.log('Received message from sns', payload);

  // if it gets this far it is a unsubscribe request
  // {
  //   "Type" : "UnsubscribeConfirmation",
  //   "MessageId" : "47138184-6831-46b8-8f7c-afc488602d7d",
  //   "Token" : "2336412f37...",
  //   "TopicArn" : "arn:aws:sns:us-west-2:123456789012:MyTopic",
  //   "Message" : "You have chosen to deactivate subscription arn:aws:sns:us-west-2:123456789012:MyTopic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55.\nTo cancel this operation and restore the subscription, visit the SubscribeURL included in this message.",
  //   "SubscribeURL" : "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:123456789012:MyTopic&Token=2336412f37fb6...",
  //   "Timestamp" : "2012-04-26T20:06:41.581Z",
  //   "SignatureVersion" : "1",
  //   "Signature" : "EXAMPLEHXgJm...",
  //   "SigningCertURL" : "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem"
  // }
});

app.listen(port, () => console.log('Sns notification listening on port ' + port + '!'));

Signatures

You should verify the authenticity of a notification, subscription confirmation, or unsubscribe confirmation message sent by Amazon SNS. We recommend using the sns-validator package when using node and javascript

For more information: https://docs.aws.amazon.com/sns/latest/dg/sns-verify-signature-of-message.html

Messages

This main thing you will care about is the Message field. This will contain the information we will be pushing to sns. This is a JSON.stringify object and will always be an object.

This section will purely talk about the Message field and all examples will show the response type for that and not the entire sns object above. Also, the guide will of JSON.parse the string already to make it really easy to understand. The data in the properties is just mock data to show the examples.

This guide will assume you already have a understanding for what the events emit on the protocol and what each data property is.

All the messages will be shaped as:

export interface PublishMessage {
  type: SnsMessageTypes;
  data: any;
}

The type maps to the response type you get back which will defined in this guide.

COLLECT_MODULE_REGISTERED

This emits when a collect module is registered.

interface Response {
  collectModule: string;
  timestamp: number;
  blockHash: string;
  blockNumber: number;
  txHash: string;
  logIndex: number;
  txIndex: number;
}

Response example

{
  "collectModule": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
  "timestamp": 1656073676000,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8,
  "logIndex": 4,
  "blockHash" "0xfe88c94d860f01a17f961bf4bdfb6e0c6cd10d3fda5cc861e805ca1240c58553"
}

MODULE_GLOBALS_TREASURY_FEE_SET

This emits when the module global treasury fee is set

interface Response {
  prevTreasuryFee: string;
  newTreasuryFee: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "prevTreasuryFee": "0x0000000000000000001",
  "newTreasuryFee": "0x0000000000000000001",
  "timestamp": 1656073676000,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

Profile related

FOLLOW_NFT_DEPLOYED

This emits when the follow nft is deployed (remember it is lazy loaded meaning only on the first follow is it deployed).

interface Response {
  profileId: string;
  followNFT: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "profileId": "0x01",
  "followNFT": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
  "timestamp": 1656073676000,
  "logIndex": 1,
  "transactionHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "transactionIndex": 8
}

METADATA_PROFILE_COMPLETE

This emits a notification when the profile metadata has been snapshotted and indexed into the API.
Refer to the @lens-protocol/metadata package for the exact metadata type definition.

import { ProfileMetadata } from '@lens-protocol/metadata';
import { ProfileMetadata as LegacyProfileMetadata } from '@lens-protocol/metadata/legacy';

interface Response {
  profileId: string;
  // the metadata extracted for you
  metadata: ProfileMetadata | LegacyProfileMetadata;
  // the snapshot s3 url for fast fetching
  snapshotLocation: string;
}

Response example

{
  "profileId": "0x06",
  "metadata": {
    "version": "1.0.0",
    "metadata_id": "cf40ca2b-945d-4fd6-9a5c-dfdfe07a7008",
    "name": "josh wagmi",
    "attributes": []
  },
  "snapshotLocation": "https://statics-polygon-lens.s3.eu-west-1.amazonaws.com/profile_metadata/0x06.json"
}

METADATA_PROFILE_FAILED

This emits a notification when the profile metadata has failed to be snapshotted. This could be because the metadata is invalid or unreachable etc. The snapshot will retry for 20 minutes if it is unreachable before it marks it as failed. If the metadata is invalid it won't retry and mark as failed with an error reason.

interface Response {
  profileId: string;
  // the reason why it failed
  errorReason: string;
  // if it was unreachable for 20 minutes
  timeoutRequest: boolean;
}

Response example

{
  "profileId": "0x06",
  "errorReason": "metadata is missing the metadata_id",
  "timeoutRequest": false
}

METADATA_PROFILE_IMAGE_NFT_REVOKED

This emits a notification when the NFT image a profile has added as a verified NFT profile picture is not owned by the profile owner anymore.

interface Response {
  // all profiles which now have been revoked the NFT on chain image. The image still lives in our snapshot
  // but when the API brings it back now its not classed as an NFT image anymore.
  profileIds: string[];
}

Response example

{
  "profileIds": ["0x06", "0x02"]
}

PROFILE_BLOCKED

This emits when a profile was blocked by another profile.

interface Response {
    byProfileId: string;
    idOfProfileBlocked: string;
    transactionExecutor: string;
    timestamp: string;
    logIndex: number;
    txHash: string;
    txIndex: number;
}

Response example

{
  "byProfileId": "0x01",
  "idOfProfileBlocked": "0x02",
  "transactionExecutor": "0xLGUA2801a31ba70dfe542793020a934F880D59aK",
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

PROFILE_UNBLOCKED

This emits when a profile was unblocked by another profile.

interface Response {
  byProfileId: string;
  idOfProfileUnblocked: string;
  transactionExecutor: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "byProfileId": "0x01",
  "idOfProfileUnblocked": "0x02",
  "transactionExecutor": "0xLGUA2801a31ba70dfe542793020a934F880D59aK",
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

PROFILE_CREATED

This emits when someone has created a new profile.

interface Response {
  profileId: string;
  creator: string;
  to: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "profileId": "0x0f",
  "creator": "0x1e9B092A67c2513DF0217A3dd5B9f13286E3D10D",
  "to": "0x1e9B092A67c2513DF0217A3dd5B9f13286E3D10D",
  "txHash": "0xee3e49b8fb85979b8885d76b9d7aa79f8e4cdded303dd3c40d993761b94957d9",
  "txIndex": 0,
  "logIndex": 1
}

PROFILE_CREATOR_ALLOWLISTED

This emits when someone has allowlisted a profile creator.

interface Response {
  profileCreator: string;
  allowlisted: boolean;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "profileCreator": "0xLGUA2801a31ba70dfe542793020a934F880D59aK",
  "allowlisted": true,
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

PROFILE_FOLLOWED

This emits when someone has follow a profile.

interface Response {
  followerProfileId: string;
  idOfProfileFollowed: string;
  followTokenIdAssigned: string;
  followModuleData: string;
  processFollowModuleReturnData: string;
  transactionExecutor: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "followerProfileId": "0x0f",
  "idOfProfileFollowed": "0x02",
  "followTokenIdAssigned": "0x08",
  "followModuleData": "0x",
  "processFollowModuleReturnData": "0x",
  "transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
  "timestamp": 1694520655000,
  "txHash": "0xb8c426ac985e329869b23210ff3ac27e34fc7eb48eee23a998d2ae6708e069c3",
  "txIndex": 71,
  "logIndex": 400
}

PROFILE_UNFOLLOWED

This emits when someone has unfollowed a profile.

interface Response {
  unfollowerProfileId: string;
  idOfProfileUnfollowed: string;
  transactionExecutor: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "unfollowerProfileId": "0x0f",
  "idOfProfileUnfollowed": "0x02",
  "transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
  "timestamp": 1694520655000,
  "txHash": "0xb8c426ac985e329869b23210ff3ac27e34fc7eb48eee23a998d2ae6708e069c3",
  "txIndex": 71,
  "logIndex": 400
}

PROFILE_GUARDIAN_STATE_CHANGED

This emits when the profile guardian state has changed.

interface Response {
  owner: string;
  enabled: boolean;
  tokenGuardianDisablingTimestamp: number;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "owner": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
  "enabled": true,
  "tokenGuardianDisablingTimestamp": 1656073958000,
  "timestamp": 1656073676000,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

PROFILE_HANDLE_LINKED

This emits when the handle was linked to the profile.

interface TokenInfo {
    id: string,
    collection: string,
}

interface Response {
  handle: TokenInfo;
  token: TokenInfo;
  transactionExecutor: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

PROFILE_HANDLE_MINTED

This emits when the new handle was minted.

interface Response {
  handle: string;
  namespace: string;
  handleId: string;
  to: number;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

PROFILE_HANDLE_UNLINKED

This emits when the handle was unlinked from the profile.

interface TokenInfo {
    id: string,
    collection: string,
}

interface Response {
    handle: TokenInfo;
    token: TokenInfo;
    transactionExecutor: string;
    timestamp: number;
    logIndex: number;
    txHash: string;
    txIndex: number;
}

PROFILE_MANAGER_CHANGED

This emits when the profile manager has changed.

interface Response {
  delegatorProfileId: string;
  configNumber: string;
  delegatedExecutors: string[];
  approvals: boolean[];
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

PROFILE_MANAGER_CONFIG_APPLIED

This emits when the profile manager config has been applied.

interface Response {
  delegatorProfileId: string;
  configNumber: string[];
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

PROFILE_METADATA_SET

This emits when someone has updated metadata for the profile. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be updated on our API. We will also broadcast this event to you so you can see what is happening in real time.

Look at METADATA_PROFILE_COMPLETE and METADATA_PROFILE_FAILED notifications to hook onto these states.

interface Response {
  profileId: string;
  // the metadata contains normally an IPFS or weblink which resolves the metadata
  metadata: string;
  transactionExecutor: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "profileId": "0x01",
  "metadata": "ipfs://Qmeu6u6Ta5qeCf6mw3zVoe9pMus96cX6eZT6dnRQKDStBT",
  "transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

PROFILE_TRANSFERRED

This emits when profile ownership was transferred to a different wallet.

interface Response {
  from: string;
  to: string;
  tokenId: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Publication related

POST_CREATED

This emits when someone has posted a new publication. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be searchable in our API. We will also broadcast this event to you so you can see what is happening in real time.

Look at METADATA_PUBLICATION_COMPLETE and METADATA_PUBLICATION_FAILED notifications to hook onto these states.

interface Response {
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  isMomoka: boolean;
  // will be defined if its a momoka post
  momokaProof?: string
    
  postParams: {
      profileId: string;
      contentURI: string;
      // will be empty array if not action modules set
      actionModules: string[];
      // will be empty array if not action modules set
      actionModulesInitDatas: string[];
      // will null if not set
      referenceModule: string | null;
      // will be null if not set
      referenceModuleInitData: string | null;
  }
  pubId: string;
  // will be empty array if not action modules set
  actionModulesInitReturnDatas: string[];
  // will be empty array if not action modules set
  referenceModuleInitReturnData: string | null;
  transactionExecutor: string;
  timestamp: number;
  // will not be defined if its a momoka post
  logIndex?: number
  txHash?: string;
  txIndex?: number;
}

Response example

{
  "serverPubId":"0x15-0x01",
  "isMomoka":false,
  "pubId":"0x01",
  "postParams":{
    "profileId": "0x12",
    "contentURI":"ipfs://QmTVj9QgJ9pSNBM7XCbCAiRfWKWj8ckCh2PWCYrExV7g3V",
    "actionModules":[],
    "actionModulesInitDatas":[],
    "referenceModule":null,
    "referenceModuleInitData":null
  },
  "actionModulesInitReturnDatas":[],
  "referenceModuleInitReturnData":null,
  "transactionExecutor":"0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
  "timestamp":1694545573000,
  "txHash":"0x359826e89fb80b6a7a6a586e9e0c8b3a80c5c797e2ec9bc96e7c7ed31897cabe",
  "txIndex":18,
  "logIndex":117
}

COMMENT_CREATED

This emits when someone has commented on a publication. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be searchable in our API. We will also broadcast this event to you so you can see what is happening in real time.

Look at METADATA_PUBLICATION_COMPLETE and METADATA_PUBLICATION_FAILED notifications to hook onto these states.

interface Response {
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  serverPubIdPointer: string;
  isMomoka: boolean;
  // will be defined if its a momoka comment
  momokaProof?: string
  commentParams: {
    profileId: string;
    contentURI: string;
    pointedProfileId: string;
    pointedPubId: string;
    referrerProfileIds: string[];
    referrerPubIds: string[];
    // will be empty array if not action modules set
    actionModules: string[];
    // will be empty array if not action modules set
    actionModulesInitDatas: string[];
    // will null if not set
    referenceModule: string | null;
    // will be null if not set
    referenceModuleData: string | null;
    // will be null if not set
    referenceModuleInitData: string | null;
  }
  pubId: string;
  // will be empty array if not action modules set
  actionModulesInitReturnDatas: string[];
  // will null if not set
  referenceModuleInitReturnData: string | null;
  // will null if not set
  referenceModuleReturnData: string | null;
  transactionExecutor: string;
  timestamp: number;
  // will not be defined if its a momoka comment
  logIndex?: number
  txHash?: string;
  txIndex?: number;
}

Response example

{
  "serverPubId": "0x15-0x02",
  "serverPubIdPointer": "0x03-0x36",
  "isMomoka": false,
  "pubId": "0x02",
  "commentParams": {
    "profileId": "0x12",
    "contentURI": "ipfs://QmTVj9QgJ9pSNBM7XCbCAiRfWKWj8ckCh2PWCYrExV7g3V",
    "pointedProfileId": "0x03",
    "pointedPubId": "0x36",
    "referrerProfileIds": [],
    "referrerPubIds": [],
    "referenceModuleData": null,
    "actionModules": [],
    "actionModulesInitDatas": [],
    "referenceModule": null,
    "referenceModuleInitData": null
  },
  "referenceModuleReturnData": null,
  "actionModulesInitReturnDatas": [],
  "referenceModuleInitReturnData": null,
  "transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
  "timestamp": 1694545895000,
  "txHash": "0xde1499c7c551f7602a3a632b622e1cf7e94a71364e8f80c9173f839a32bf187b",
  "txIndex": 16,
  "logIndex": 127
}

MIRROR_CREATED

This emits when a profile has mirrored a publication. This inherits the mirrored publication metadata. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be searchable in our API. We will also broadcast this event to you so you can see what is happening in real time.

Look at METADATA_PUBLICATION_COMPLETE and METADATA_PUBLICATION_FAILED notifications to hook onto these states.
// TODO: Update this once mirror has own metadata
Please note mirror is different to post and comment in terms of it doesn't have any metadata linked to it. You must on your end link the publication it has mirrored to the mirror itself to know when its snapshotted and viewable.

interface Response {
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  serverPubIdPointer: string;
  isMomoka: boolean;
  // will be defined if its a momoka mirror
  momokaProof?: string
  mirrorParams: {
    profileId: string;
    pointedProfileId: string;
    pointedPubId: string;
    referrerProfileIds: string[];
    referrerPubIds: string[];
    // will be null if not set
    referenceModuleData: string | null;
  }
  pubId: string;
  // will null if not set
  referenceModuleReturnData: string | null;
  transactionExecutor: string;
  timestamp: number;
  // will not be defined if its a momoka mirror
  logIndex?: number
  txHash?: string;
  txIndex?: number;
}

Response example

{
  "serverPubId": "0x15-0x03",
  "serverPubIdPointer": "0x03-0x36",
  "isMomoka": false,
  "pubId": "0x03",
  "mirrorParams": {
    "profileId": "0x12",
    "pointedProfileId": "0x36",
    "pointedPubId": "0x24",
    "referrerProfileIds": [],
    "referrerPubIds": [],
    "referenceModuleData": null
  },
  "referenceModuleReturnData": null,
  "transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
  "timestamp": 1694546347000,
  "txHash": "0x3d4253698cc528b7178ebdc57619e9f3e2867c0515e2ebce06851f8aa339a776",
  "txIndex": 9,
  "logIndex": 79
}

QUOTE_CREATED

This emits when someone has quoted a publication. Please note this does not mean it follows metadata standards, and we allow it in. We index it but if it does not follow our metadata standards it will not be searchable in our API. We will also broadcast this event to you so you can see what is happening in real time.

Look at METADATA_PUBLICATION_COMPLETE and METADATA_PUBLICATION_FAILED notifications to hook onto these states.

interface Response {
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  serverPubIdPointer: string;
  isMomoka: boolean;
  // will be defined if its a momoka quote
  momokaProof?: string
  quoteParams: {
    profileId: string;
    contentURI: string;
    pointedProfileId: string;
    pointedPubId: string;
    referrerProfileIds: string[];
    referrerPubIds: string[];
    // will be empty array if not action modules set
    actionModules: string[];
    // will be empty array if not action modules set
    actionModulesInitDatas: string[];
    // will null if not set
    referenceModule: string | null;
    // will be null if not set
    referenceModuleData: string | null;
    // will be null if not set
    referenceModuleInitData: string | null;
  }
  pubId: string;
  // will be empty array if not action modules set
  actionModulesInitReturnDatas: string[];
  // will null if not set
  referenceModuleInitReturnData: string | null;
  // will null if not set
  referenceModuleReturnData: string | null;
  transactionExecutor: string;
  timestamp: number;
  // will not be defined if its a momoka quote
  logIndex?: number
  txHash?: string;
  txIndex?: number;
}

Response example

{
  "serverPubId": "0x15-0x02",
  "serverPubIdPointer": "0x03-0x36",
  "isMomoka": false,
  "pubId": "0x02",
  "quoteParams": {
    "profileId": "0x12",
    "contentURI": "ipfs://QmTVj9QgJ9pSNBM7XCbCAiRfWKWj8ckCh2PWCYrExV7g3V",
    "pointedProfileId": "0x03",
    "pointedPubId": "0x36",
    "referrerProfileIds": [],
    "referrerPubIds": [],
    "referenceModuleData": null,
    "actionModules": [],
    "actionModulesInitDatas": [],
    "referenceModule": null,
    "referenceModuleInitData": null
  },
  "referenceModuleReturnData": null,
  "actionModulesInitReturnDatas": [],
  "referenceModuleInitReturnData": null,
  "transactionExecutor": "0xf011768dfE47eb9D2071729238e3B80029d8Ebc9",
  "timestamp": 1694545895000,
  "txHash": "0xde1499c7c551f7602a3a632b622e1cf7e94a71364e8f80c9173f839a32bf187b",
  "txIndex": 16,
  "logIndex": 127
}

METADATA_PUBLICATION_COMPLETE

This emits a notification when the publication metadata has been snapshotted and indexed into the API.
Refer to the @lens-protocol/metadata package for the exact metadata type definition.

import { PublicationMetadata } from '@lens-protocol/metadata';
import { PublicationMetadata as LegacyPublicationMetadata } from '@lens-protocol/metadata/legacy';

interface Response {
  profileId: string;
  pubId: string;
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  // this is the mirror publication id
  serverPubId: string;
  // the metadata extracted for you
  metadata: PublicationMetadata | LegacyPublicationMetadata;
  // the snapshot url for fast fetching
  snapshotLocation: string;
}

Response example

{
  "profileId": "0x06",
  "pubId": "0x02",
  "serverPubId": "0x06-0x02",
  "metadata": {
    "description":"My text Description",
    "external_url":"https://mytext.com",
    "name":"My text3",
    "image":"https://text.com/image.png",
    "$schema":"https://json-schemas.lens.dev/publications/text-only/3.0.0.json",
    "lens":{
      "id":"1030ee6e-51cb-4a09-a74a-abdccc6ef890"
    },
    "appId":"my-app-id",
    "content":"My text Content",
    "locale":"en-US",
    "tags":["text"],
    "mainContentFocus":"TEXT_ONLY"
  },
  "snapshotLocation": "https://statics-polygon-lens.s3.eu-west-1.amazonaws.com/public/0x06-0x02.json"
}

METADATA_PUBLICATION_FAILED

This emits a notification when the publication metadata has failed to be snapshotted. This could be because the metadata is invalid or unreachable etc. The snapshot will retry for 20 minutes if it is unreachable before it marks it as failed. If the metadata is invalid it won't retry and mark as failed with a valid error reason.

interface Response {
  profileId: string;
  pubId: string;
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  // this is the mirror publication id
  serverPubId: string;
  // the reason why it failed
  errorReason: string;
  // if it was unreachable for 20 minutes
  timeoutRequest: boolean;
}

Response example

{
  "profileId": "0x06",
  "pubId": "0x02",
  "serverPubId": "0x06-0x02",
  "errorReason": "metadata is missing the metadata_id",
  "timeoutRequest": false
}

PROFILE_MENTIONED

This emits a notification when a profile has been mentioned in a publication. This is used to notify the profile that they have been mentioned in a publication.

interface Response {
  profileIdPointed: string;
  pubIdPointed: string;
  // pub id are counters of the publication, so they clash with profiles
  // on the server, we build up our publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  // this will be the publication the mention was created on
  serverPubIdPointer: string
  handle: string;
  profileId: string;
  content: string;
  contentURI: string;
  snapshotLocation: string;
  timestamp: Date;
}

PUBLICATION_ACTED

This emits when a profile acted on a publication.

interface Response {
  publicationActionParams: {
    publicationActedProfileId: string;
    publicationActedId: string;
    actorProfileId: string;
    referrerProfileIds: string[];
    referrerPubIds: string[];
    actionModuleAddress: string;
    actionModuleData: string;
  };
  actionModuleReturnData: string;
  transactionExecutor: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

PUBLICATION_COLLECTED

This emits when a publication is collected.

interface Response {
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  collectedProfileId: string;
  collectedPubId: string;
  collectorProfileId: string;
  nftRecipient: string;
  collectActionData: string;
  collectActionResult: string;
  collectNFT: string;
  tokenId: string;
  collectNFT: string;
  collectNFT: string;
  transactionExecutor: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

PUBLICATION_REACTION_ADDED

This emits a notification when a profile has added a reaction to a publication. This will also add as a toggle so what we mean by that is if someone called UPVOTE then pressed DOWNVOTE the last UPVOTE is now not valid. A reaction a profile does on a publication is only allowed to be 1.

interface Response {
  profileId: string;
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  type: string;
  // unix timestamp
  reactedAt: number;
}

PUBLICATION_REACTION_REMOVED

This emits a notification when a profile removes a reaction on a publication.

interface Response {
  profileId: string;
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  type: string;
}

PUBLICATION_HIDDEN

This emits a notification when a profile has hidden a publication. Please note the API still returns these publication to not break threads but hides the content and media of it. These do not get returned in the timelines, search, profiles and explore queries though.

interface Response {
  profileId: string;
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
}

PUBLICATION_REPORTED

This emits a notification when a publication is reported.

interface Response {
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  profileId: string;
  reason: string;
  subreason: string;
  additionalComments: string | null;
}

COLLECT_NFT_TRANSFERRED

This emits when the collect nft is transferred to a new owner, remember this is emitted when minted as well.

interface Response {
  profileId: string;
  pubId: string;
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  // each collect NFT minted has a token id the first one is 1 then 2 just like a normal NFT project
  collectNFTId: string;
  from: string;
  to: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Protocol related

COLLECT_NFT_DEPLOYED

This emits when the collect nft is deployed (remember it is lazy loaded meaning only on the first collect is it deployed).

interface Response {
  profileId: string;
  pubId: string;
  // pub id are counters of the publication so they clash with profiles
  // on the server we build up our own publication id to allow it to be searchable
  // this is {profileId}-{pubId} and is used in all our API calls and responses
  serverPubId: string;
  collectNFT: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "profileId": "0x01",
  "pubId": "0x02",
  "serverPubId": "0x01-0x02",
  "collectNFT": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

COLLECT_OPEN_ACTION_MODULE_ALLOWLISTED

This emits when an open action module is allowlisted or removed from allowlist.

interface Response {
  moduleName: string;
  collectModule: string;
  whitelist: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

CURRENCY_MODULE_ALLOWLISTED

This emits when a currency module is allowlisted or removed from allowlist.

interface Response {
  currency: string;
  name: string;
  symbol: string;
  decimals: number;
  prevWhitelisted: boolean;
  whitelisted: boolean;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "currency": "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889",
  "name": "Wrapped Matic",
  "symbol": "WMATIC",
  "decimals": 18,
  "prevWhitelisted": false,
  "whitelisted": true,
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

FOLLOW_MODULE_ALLOWLISTED

This emits when a follow module is allowlisted or removed from allowlist.

interface Response {
  followModule: string;
  whitelisted: boolean;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "followModule": "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889",
  "whitelisted": true,
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

FOLLOW_MODULE_SET

This emits when a profile changes their follow module.

interface Response {
  profileId: string;
  followModule: string;
  followModuleReturnData: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "profileId": "0x01",
  "followModule": "0xe7AB9BA11b97EAC820DbCc861869092b52B65C06",
  "followModuleReturnData": "0x00000000000000000000000000000000000000000000000000000000000027100000000000000000000000002058a9d7613eee744279e3856ef0eada5fcbaa7e0000000000000000000000001d441b02fa5a2b609a83ccb5df1bfeba91a0794e",
  "timestamp": 1656073676000,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

REFERENCE_MODULE_ALLOWLISTED

This emits when a reference module is allowlisted or unallowlisted.

interface Response {
  referenceModule: string;
  referenceModuleName: string;
  whitelisted: boolean;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "referenceModule": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
  "referenceModuleName": "FollowerOnlyReferenceModule",
  "allowlisted": true,
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

OPEN_ACTION_MODULE_ALLOWLISTED

This emits when a new open action module is allowlisted or unallowlisted

interface Response {
  actionModule: string;
  openActionModuleName: string;
  id: string;
  whitelisted: boolean;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

PROTOCOL_LENS_HUB_ADDRESS_SIG_NONCE_UPDATED

This emits when a lens hub address signature nonce is updated.

interface Response {
  signer: string;
  nonce: string;
  timestamp: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

PROTOCOL_TOKEN_HANDLE_ADDRESS_SIG_NONCE_UPDATED

This emits when a token handle address signature nonce is updated.

interface Response {
    signer: string;
    nonce: string;
    timestamp: number;
    logIndex: number;
    txHash: string;
    txIndex: number;
}

PROTOCOL_STATE_CHANGED

This emits when the state of the protocol is changed.

0: Unpaused
1: PublishingPaused
2: Paused

interface Response {
  caller: string;
  prevState: number;
  newState: number;
  logIndex: number;
  txHash: string;
  txIndex: number;
}

Response example

{
  "caller": "0xFCDA2801a31ba70dfe542793020a934F880D54aB",
  "prevState": 2,
  "newState": 0,
  "allowlisted": true,
  "timestamp": 1656073676000,
  "logIndex": 1,
  "txHash": "0x3adc83ab8289bf3389417ff25d27ba9ef843d3981554536b462d3cba3724f837",
  "txIndex": 8
}

Media related

MEDIA_SNAPSHOTTED

This emits when a media is successfully snapshotted. This can be triggered when
a publication with media gets posted, or when a profile updates their metadata.
In the former, the source will be the PublicationId and on the latter it will
be the ProfileId.

interface Response {
  originalUrl: string;
  snapshottedUrl: string;
  source: string;
}

Response example

{
  "originalUrl": "https://my-media.com/image.png",
  "snapshottedUrl": "https://statics-polygon-lens.s3.eu-west-1.amazonaws.com/media/0x01-0x02.png",
  "source": "0x01-0x02"
}

MEDIA_SNAPSHOT_FAILED

This emits when a media fails to be snapshotted. This can happen due to timing
out after all retry attempts, due to url being invalid or content unavailable
at the time (404 or IPFS pinning unfinished).

interface Response {
  originalUrl: string;
  source: string;
  message: string;
}

Response example

{
  "originalUrl": "https://my-media.com/image.png",
  "source": "0x01-0x02",
  "message": "Snapshot failed: Failed to fetch"
}