Skip to content

API — Episodes

The episodes endpoints provide access to conversation history as discrete episodes. Each episode represents a conversation thread between a user and the AI assistant. The API supports listing episodes, viewing transcripts, looking up by response ID, tracking unread state, and a BFF transformation layer that reshapes episode data for mobile card rendering.

Architecture: Rover-agent acts as a BFF (Backend for Frontend) that proxies requests to the consumer-agent Python service. BFF endpoints transform the response into an ordered array shape optimized for mobile card rendering.


All episodes endpoints require authentication via the authenticatedAPI middleware chain.

Required Headers:

  • X-API-Key — API key for service authentication
  • Authorization — JWT bearer token for user authentication
  • userId — User identifier (extracted from JWT in production)

MethodPathDescription
GET/api/v1/episodesList episodes
GET/api/v1/episodes/{episode_id}Get episode transcript
GET/api/v1/episodes/unread/countGet unread episode count
GET/api/v1/episodes/unreadList unread episodes with transcripts
GET/api/v1/episodes/bffEpisodes BFF
GET/api/v1/episodes/bff/unreadUnread episodes BFF

List the authenticated user’s episodes sorted by most recent activity.

ParameterTypeDefaultDescription
limitint20Number of episodes to return (1–100)
cursorstringPagination cursor from previous response
{
"episodes": [
{
"id": "968aed30-a619-4ac5-a00d-f4a3a581e9cd",
"title": "Daily Deals Alert",
"displayDate": "3 hours ago",
"is_unread": true,
"image_url": "https://cdn.fetchrewards.com/test/hero-deals.png",
"icon_url": "https://cdn.fetchrewards.com/test/icon-deals.png",
"subtitle": "3 new offers near you",
"cta_label": "See Deals",
"cta_action": "deeplink://offers/daily"
},
{
"id": "c0c48608-c1b4-479d-b75a-98a7acb1cdda",
"title": "What cereal has the most points?",
"displayDate": "Yesterday",
"is_unread": false,
"image_url": null,
"icon_url": null,
"subtitle": null,
"cta_label": null,
"cta_action": null
}
],
"pagination": {
"next_cursor": "eyJVc2VySWQiOi..."
},
"unread_count": 3
}

EpisodeSummary:

FieldTypeDescription
idstringUnique episode identifier (UUID)
titlestringEpisode title — first user message or explicitly set title, truncated to 120 chars
displayDatestringRelative timestamp: “Just now”, “5 minutes ago”, “3 hours ago”, “Yesterday”, “3 days ago”, “Jan 15”
is_unreadbooltrue if the episode was created by the episode builder and has not been opened
image_urlstring | nullHero image URL for BFF card rendering (set by episode builder)
icon_urlstring | nullIcon URL for BFF card rendering (set by episode builder)
subtitlestring | nullSubtitle text for BFF card (set by episode builder)
cta_labelstring | nullCTA button label (set by episode builder)
cta_actionstring | nullCTA deeplink URL (set by episode builder)

Pagination:

FieldTypeDescription
next_cursorstring | nullOpaque cursor for fetching the next page. null when no more pages.

Get the full transcript for a specific episode. Returns conversational turns (user message + AI response events). Side effect: marks unread episodes as read in the background.

ParameterTypeDescription
episode_idstringEpisode UUID
ParameterTypeDefaultDescription
limitint20Number of turns to return (1–100)
cursorstringPagination cursor for turn pagination
{
"id": "968aed30-a619-4ac5-a00d-f4a3a581e9cd",
"turns": [
{
"user_query": "Show me today's deals",
"ai_response": [
{"event_type": "chunk", "content": "Here are some great deals!"},
{
"event_type": "data_loaded",
"data": {
"type": "offer_list",
"key": {"ids": [{"id": "offer_001"}, {"id": "offer_002"}]},
"items": [{"offerId": "offer_001"}, {"offerId": "offer_002"}]
}
}
]
}
],
"pagination": {
"next_cursor": null
}
}
  • Product cards in the transcript are enriched with current product data (title, price, image) via the FIDO Product Service.
  • Prompt-suggestion components and reasoning blocks are stripped from the transcript.
  • Opening a transcript marks the episode as read (background task, does not block the response).

Get the count of unread episodes for the authenticated user.

{
"unread_count": 3
}
FieldTypeDescription
unread_countintNumber of episodes created by the episode builder that have not been opened
  • Use this endpoint for badge counts on navigation tabs or app icons.
  • The count only includes episodes created via the episode builder (POST /episodes), not user-initiated conversations.
  • An episode becomes “read” when its transcript is fetched via GET /api/v1/episodes/{episode_id}.

List unread episodes with their full transcripts.

ParameterTypeDefaultDescription
limitint10Number of episodes to return (1–20)
cursorstringPagination cursor
{
"episodes": [
{
"id": "968aed30-a619-4ac5-a00d-f4a3a581e9cd",
"title": "Daily Deals Alert",
"displayDate": "3 hours ago",
"turns": [
{
"user_query": "Show me today deals",
"ai_response": [
{"event_type": "chunk", "content": "We found some great deals for you today!"},
{"event_type": "data_loaded", "data": {"type": "offer_list", "key": {"ids": [{"id": "offer_001"}]}}}
]
}
]
}
],
"pagination": {
"next_cursor": null
},
"unread_count": 3
}
  • This endpoint does not mark episodes as read (unlike GET /api/v1/episodes/{episode_id}).
  • Transcripts include full turn data with product card enrichment.

BFF endpoint. Returns episodes grouped into time-based sections optimized for mobile card rendering. Applies default values for missing BFF fields and compacts display timestamps.

ParameterTypeDefaultDescription
limitint20Number of episodes to return
cursorstringPagination cursor
{
"unread_count": 3,
"sections": [
{
"header": "Today",
"episodes": [
{
"id": "968aed30-a619-4ac5-a00d-f4a3a581e9cd",
"title": "Daily Deals Alert",
"subtitle": "3 new offers near you",
"display_time": "3h",
"image_url": "https://cdn.fetchrewards.com/test/hero-deals.png",
"icon_url": "https://cdn.fetchrewards.com/test/icon-deals.png",
"badge_icon_url": "https://cdn.fetchrewards.com/images/episode-badge.png",
"is_unread": true,
"cta": {
"label": "See Deals",
"action": "deeplink://offers/daily"
},
"primary_action": "deeplink://episode/968aed30-a619-4ac5-a00d-f4a3a581e9cd"
}
]
},
{
"header": "Yesterday",
"episodes": [
{
"id": "dad43180-c301-41d9-a37d-97486f2679af",
"title": "Snack Suggestions",
"display_time": "1d",
"image_url": "https://cdn.fetchrewards.com/images/episode-default-hero.png",
"icon_url": "https://cdn.fetchrewards.com/images/episode-default-icon.png",
"is_unread": false,
"cta": {
"label": "View",
"action": "deeplink://episode/dad43180-c301-41d9-a37d-97486f2679af"
},
"primary_action": "deeplink://episode/dad43180-c301-41d9-a37d-97486f2679af"
}
]
}
],
"pagination": {
"next_cursor": "eyJVc2VySWQiOi...",
"has_more": true
}
}

BFF Response (top-level):

FieldTypeDescription
unread_countintTotal unread episodes (same value regardless of pagination)
sectionsBffSection[]Time-based sections containing episodes. Empty sections are omitted. Order: Today → Yesterday → In the past.
paginationBffPaginationPagination info

BffSection:

FieldTypeDescription
headerstringSection header: "Today", "Yesterday", or "In the past"
episodesBffEpisode[]Episodes in this section (preserves sort order from consumer-agent)

Section Classification:

displayDate patternSection
”Just now”Today
”X minute(s) ago”Today
”X hour(s) ago”Today
”Yesterday”Yesterday
”1 day ago”Yesterday
”X days ago” (X > 1)In the past
Absolute dates (“Jan 15”)In the past

BffEpisode:

FieldTypeRequiredDescription
idstringyesEpisode UUID
titlestringyesEpisode title
subtitlestringnoSubtitle text. Omitted if not set.
display_timestringyesCompact timestamp: "now", "5m", "3h", "1d", "Jan 15"
image_urlstringyesHero image URL. Falls back to default: episode-default-hero.png
icon_urlstringyesIcon URL. Falls back to default: episode-default-icon.png
badge_icon_urlstringnoBadge overlay icon. Only present when image_url or icon_url was explicitly set (not defaulted).
is_unreadboolyesWhether the episode has been opened
ctaCTAyesCall-to-action button
primary_actionstringyesAlways deeplink://episode/{id}

CTA:

FieldTypeDescription
labelstringButton text. Falls back to "View" if not set.
actionstringDeeplink URL. Falls back to deeplink://episode/{id} if not set.

BffPagination:

FieldTypeDescription
next_cursorstring | nullOpaque cursor for next page
has_morebooltrue if more pages are available

The BFF transforms consumer-agent’s relative timestamps into compact mobile format:

InputOutput
”Just now”"now"
”5 minutes ago”"5m"
”1 minute ago”"1m"
”3 hours ago”"3h"
”1 hour ago”"1h"
”Yesterday”"1d"
”4 days ago”"4d"
”Jan 15”"Jan 15" (passthrough)

When episodes are created without explicit BFF fields (e.g., user-initiated conversations), the BFF applies defaults:

FieldDefault Value
image_urlhttps://cdn.fetchrewards.com/images/episode-default-hero.png
icon_urlhttps://cdn.fetchrewards.com/images/episode-default-icon.png
badge_icon_urlomitted (only set for episodes with custom image/icon)
cta.label"View"
cta.actiondeeplink://episode/{id}
  • Render sections in array order (Today → Yesterday → In the past). Empty sections are omitted from the response.
  • Use section header as a section divider label in the feed UI.
  • Render episodes within each section in array order.
  • Use has_more to show/hide “load more” UI.
  • Use unread_count for badge display.
  • primary_action is the deeplink to open the episode transcript.
  • cta.action may differ from primary_action for episodes with custom CTAs (e.g., deeplink://offers/daily).

Same as GET /api/v1/episodes/bff, but filtered to unread episodes only. Uses the same response shape with sections.

Same as /api/v1/episodes/bff.

Same shape as /api/v1/episodes/bff. Only episodes where is_unread=true are included. Sections that become empty after filtering are omitted.


Missing or invalid authentication credentials.

{
"error": "Unauthorized"
}

Python agent (consumer-agent) is not configured.

Episodes endpoints require Python agent configuration

Consumer-agent is unreachable or returned an unparseable response.

Failed to proxy request to Python agent

If consumer-agent returns a non-200 status (e.g., 400, 404, 422), the response is passed through to the client unchanged.


  • Fields unknown to the client must be ignored.
  • New optional fields may be added to BffEpisode or BffSection without version bump.
  • The sections and episodes arrays may contain new fields without notice.
  • Breaking changes to required fields will use a new API version path.