Skip to main content

Overview

The RSS Subscriptions API allows you to subscribe channels to RSS or Atom feeds. New feed items are automatically posted to the subscribed channel at regular intervals. Hazel Chat polls feeds periodically and posts new items as messages. Each subscription tracks the last fetched item to avoid duplicates.

rssSubscription.create

Subscribe a channel to an RSS or Atom feed.
channelId
ChannelId
required
Channel to receive feed items
feedUrl
string
required
URL of the RSS or Atom feed
pollingIntervalMinutes
number
default:15
How often to check the feed for new items (in minutes)
data
RssSubscription
The created subscription
transactionId
TransactionId
Transaction ID for optimistic updates
Errors:
  • ChannelNotFoundError - Channel doesn’t exist
  • RssSubscriptionExistsError - Channel is already subscribed to this feed
  • RssFeedValidationError - Feed URL is invalid or feed cannot be parsed
  • UnauthorizedError - User is not authorized
  • InternalServerError - Server error
const result = await client.rpc("rssSubscription.create", {
  channelId: "channel_123",
  feedUrl: "https://blog.example.com/feed.xml",
  pollingIntervalMinutes: 30 // Check every 30 minutes
})

console.log("Subscribed to:", result.data.feedTitle)
console.log("Polling every:", result.data.pollingIntervalMinutes, "minutes")

rssSubscription.list

List all RSS subscriptions for a specific channel.
channelId
ChannelId
required
Channel identifier
data
RssSubscription[]
Array of subscriptions for the channel
Errors:
  • ChannelNotFoundError - Channel doesn’t exist
  • UnauthorizedError - User is not authorized
  • InternalServerError - Server error
const result = await client.rpc("rssSubscription.list", {
  channelId: "channel_123"
})

for (const sub of result.data) {
  console.log(`Feed: ${sub.feedTitle || sub.feedUrl}`)
  console.log(`Status: ${sub.isEnabled ? "Active" : "Disabled"}`)
  if (sub.consecutiveErrors > 0) {
    console.log(`⚠️  ${sub.consecutiveErrors} consecutive errors`)
  }
}

rssSubscription.listByOrganization

List all RSS subscriptions across all channels in your organization.
data
RssSubscription[]
Array of all subscriptions in the organization
Errors:
  • UnauthorizedError - User is not authenticated
  • InternalServerError - Server error
const result = await client.rpc("rssSubscription.listByOrganization", {})

console.log(`Total RSS subscriptions: ${result.data.length}`)

// Find feeds with errors
const failing = result.data.filter(s => s.consecutiveErrors > 3)
console.log(`Feeds with errors: ${failing.length}`)

for (const sub of failing) {
  console.log(`❌ ${sub.feedUrl}: ${sub.lastErrorMessage}`)
}

rssSubscription.update

Update an RSS subscription’s settings.
id
RssSubscriptionId
required
Subscription identifier
isEnabled
boolean
Enable or disable the subscription
pollingIntervalMinutes
number
New polling interval in minutes
data
RssSubscription
Updated subscription object
transactionId
TransactionId
Transaction ID for optimistic updates
Errors:
  • RssSubscriptionNotFoundError - Subscription doesn’t exist
  • UnauthorizedError - User is not authorized
  • InternalServerError - Server error
await client.rpc("rssSubscription.update", {
  id: "rsssub_abc123",
  isEnabled: false
})

console.log("Feed temporarily paused")

rssSubscription.delete

Delete an RSS subscription (soft delete). The channel will stop receiving feed items.
id
RssSubscriptionId
required
Subscription identifier
transactionId
TransactionId
Transaction ID for optimistic updates
Errors:
  • RssSubscriptionNotFoundError - Subscription doesn’t exist
  • UnauthorizedError - User is not authorized
  • InternalServerError - Server error
await client.rpc("rssSubscription.delete", {
  id: "rsssub_abc123"
})

console.log("RSS subscription removed")

Polling Behavior

How Polling Works

  1. Hazel Chat fetches the feed at the specified interval
  2. Compares new items against lastItemGuid to detect duplicates
  3. Posts new items to the channel
  4. Updates lastFetchedAt and lastItemPublishedAt
  5. On error, increments consecutiveErrors and stores error message

Error Handling

When a feed fails to fetch:
  • consecutiveErrors is incremented
  • lastErrorMessage and lastErrorAt are updated
  • Polling continues at the same interval
  • When fetch succeeds, consecutiveErrors resets to 0
const result = await client.rpc("rssSubscription.list", {
  channelId: "channel_123"
})

for (const sub of result.data) {
  if (sub.consecutiveErrors >= 5) {
    console.log(`⚠️  Feed may be broken: ${sub.feedUrl}`)
    console.log(`Last error: ${sub.lastErrorMessage}`)
    console.log(`Failed since: ${sub.lastErrorAt}`)
    
    // Consider disabling the subscription
    await client.rpc("rssSubscription.update", {
      id: sub.id,
      isEnabled: false
    })
  }
}

// Check every 5-15 minutes
await client.rpc("rssSubscription.create", {
  channelId: "news_channel",
  feedUrl: "https://news.ycombinator.com/rss",
  pollingIntervalMinutes: 10
})

Example: Multi-Feed News Channel

const newsFeeds = [
  { url: "https://news.ycombinator.com/rss", interval: 15 },
  { url: "https://lobste.rs/rss", interval: 30 },
  { url: "https://www.reddit.com/r/programming/.rss", interval: 20 },
  { url: "https://thenewstack.io/feed/", interval: 60 }
]

const channelId = "tech_news_channel"

for (const feed of newsFeeds) {
  try {
    await client.rpc("rssSubscription.create", {
      channelId,
      feedUrl: feed.url,
      pollingIntervalMinutes: feed.interval
    })
    console.log(`✓ Subscribed to ${feed.url}`)
  } catch (error) {
    if (error._tag === "RssFeedValidationError") {
      console.error(`✗ Invalid feed: ${feed.url}`, error.message)
    } else if (error._tag === "RssSubscriptionExistsError") {
      console.log(`- Already subscribed: ${feed.url}`)
    } else {
      throw error
    }
  }
}

Build docs developers (and LLMs) love