Skip to content

Cards API#

The Cards endpoint allows you to retrieve cards from your Storyteller tenant. Cards are interactive content elements that can contain various types of media and information.

Endpoint Details#

GET https://integrations.usestoryteller.com/api/cards

Headers#

Header Required Description
x-storyteller-api-key Yes Your API key for authentication

Query Parameters#

Parameter Type Required Default Description
searchText string No - Filter cards by title or content
externalId string No - Filter cards by external ID
currentPage integer No 1 Page number for pagination
pageSize integer No 10 Number of items per page (max 500)
sort string No - Sort order: AlphabeticalAsc, LastModifiedDesc, CreatedDesc, PublishedDesc

Response#

Success Response (200 OK)#

{
  "cards": [
    {
      "id": "3e9444d1-1a21-4d3e-cbdc-3a0986ec9838",
      "internalTitle": "Product Showcase Card",
      "title": "New Product Launch",
      "subtitle": "Discover our latest innovation",
      "externalId": "product-card-001",
      "cardType": "product",
      "duration": 15,
      "createdAt": "2024-03-01T10:30:00Z",
      "publishedAt": "2024-03-15T12:00:00Z",
      "cmsUrl": "https://yourtenant.usestoryteller.com/cards/3e9444d1-1a21-4d3e-cbdc-3a0986ec9838/form",
      "cmsAnalyticsUrl": "https://yourtenant.usestoryteller.com/cards/3e9444d1-1a21-4d3e-cbdc-3a0986ec9838/analytics",
      "categories": [
        {
          "title": "Products",
          "externalId": "products"
        }
      ],
      "collections": [
        {
          "id": "collection-id-123",
          "title": "Featured Products",
          "externalId": "featured-products"
        }
      ]
    }
  ],
  "pageSize": 10,
  "currentPage": 1,
  "totalPages": 1
}

Response Fields#

Card Object#

Field Type Description
id string Unique card identifier
internalTitle string Internal title for content management
title string Public display title of the card
subtitle string Card subtitle or description
externalId string External identifier for the card
cardType string Type of card (e.g., "product", "info", "video")
duration integer Duration in seconds for video cards
createdAt string ISO timestamp when card was created
publishedAt string ISO timestamp when card was published
cmsUrl string URL to edit the card in Storyteller CMS
cmsAnalyticsUrl string URL to view card analytics
categories array Array of category objects assigned to the card
collections array Array of collection objects containing the card

Category Object#

Field Type Description
title string Category display name
externalId string External identifier for the category

Collection Object#

Field Type Description
id string Unique collection identifier
title string Collection display name
externalId string External identifier for the collection

Pagination Object#

Field Type Description
pageSize integer Number of items per page
currentPage integer Current page number
totalPages integer Total number of pages available

Code Examples#

const fetch = require('node-fetch');

async function getCards(searchText = '', externalId = '', currentPage = 1, pageSize = 10, sort = '') {
  const params = new URLSearchParams({
    ...(searchText && { searchText }),
    ...(externalId && { externalId }),
    currentPage: currentPage.toString(),
    pageSize: pageSize.toString(),
    ...(sort && { sort })
  });

  try {
    const response = await fetch(`https://integrations.usestoryteller.com/api/cards?${params}`, {
      method: 'GET',
      headers: {
        'x-storyteller-api-key': process.env.STORYTELLER_API_KEY
      }
    });

    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(`HTTP ${response.status}: ${errorData.detail}`);
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error fetching cards:', error);
    throw error;
  }
}

// Usage examples
async function examples() {
  // Get all cards
  const allCards = await getCards();
  console.log(`Found ${allCards.cards.length} cards`);

  // Search for specific cards
  const searchResults = await getCards('product', '', 1, 20);
  console.log(`Found ${searchResults.cards.length} cards matching "product"`);

  // Filter by external ID
  const filteredCards = await getCards('', 'product-card-001', 1, 20);
  console.log(`Found ${filteredCards.cards.length} cards with external ID "product-card-001"`);

  // Get cards sorted by publish date
  const sortedCards = await getCards('', '', 1, 50, 'PublishedDesc');
  console.log(`Retrieved ${sortedCards.cards.length} cards sorted by publish date`);

  // Get card IDs and types
  const cardInfo = allCards.cards.map(card => ({
    id: card.id,
    type: card.cardType,
    title: card.title
  }));
  console.log('Available cards:', cardInfo);
}
import requests
import os
from urllib.parse import urlencode

def get_cards(search_text='', external_id='', current_page=1, page_size=10, sort=''):
    url = 'https://integrations.usestoryteller.com/api/cards'
    headers = {
        'x-storyteller-api-key': os.environ.get('STORYTELLER_API_KEY')
    }

    params = {
        'currentPage': current_page,
        'pageSize': page_size
    }

    if search_text:
        params['searchText'] = search_text
    if external_id:
        params['externalId'] = external_id
    if sort:
        params['sort'] = sort

    try:
        response = requests.get(url, headers=headers, params=params)
        response.raise_for_status()

        data = response.json()
        return data
    except requests.exceptions.RequestException as e:
        print(f'Error fetching cards: {e}')
        if hasattr(e.response, 'json'):
            print(f'Error details: {e.response.json()}')
        raise

# Usage examples
try:
    # Get all cards
    all_cards = get_cards()
    print(f'Found {len(all_cards["cards"])} cards')

    # Search for specific cards
    search_results = get_cards(search_text='product', page_size=20)
    print(f'Found {len(search_results["cards"])} cards matching "product"')

    # Filter by external ID
    filtered_cards = get_cards(external_id='product-card-001', page_size=20)
    print(f'Found {len(filtered_cards["cards"])} cards with external ID "product-card-001"')

    # Get cards sorted by publish date
    sorted_cards = get_cards(page_size=50, sort='PublishedDesc')
    print(f'Retrieved {len(sorted_cards["cards"])} cards sorted by publish date')

    # Extract card information
    card_info = [{
        'id': card['id'],
        'type': card['cardType'],
        'title': card['title']
    } for card in all_cards['cards']]
    print('Available cards:', card_info)

except Exception as e:
    print(f'Failed to fetch cards: {e}')
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using Newtonsoft.Json;

public class CardsClient
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl = "https://integrations.usestoryteller.com";

    public CardsClient(string apiKey)
    {
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("x-storyteller-api-key", apiKey);
    }

    public async Task<CardsResponse> GetCardsAsync(string searchText = "", string externalId = "", int currentPage = 1, int pageSize = 10, string sort = "")
    {
        try
        {
            var queryParams = new List<string>
            {
                $"currentPage={currentPage}",
                $"pageSize={pageSize}"
            };

            if (!string.IsNullOrEmpty(searchText))
            {
                queryParams.Add($"searchText={Uri.EscapeDataString(searchText)}");
            }

            if (!string.IsNullOrEmpty(externalId))
            {
                queryParams.Add($"externalId={Uri.EscapeDataString(externalId)}");
            }

            if (!string.IsNullOrEmpty(sort))
            {
                queryParams.Add($"sort={sort}");
            }

            var queryString = string.Join("&", queryParams);
            var response = await _httpClient.GetAsync($"{_baseUrl}/api/cards?{queryString}");
            response.EnsureSuccessStatusCode();

            var responseContent = await response.Content.ReadAsStringAsync();
            var cards = JsonConvert.DeserializeObject<CardsResponse>(responseContent);

            return cards;
        }
        catch (HttpRequestException ex)
        {
            throw new Exception($"Error fetching cards: {ex.Message}", ex);
        }
    }
}

public class CardsResponse
{
    public Card[] Cards { get; set; }
    public int PageSize { get; set; }
    public int CurrentPage { get; set; }
    public int TotalPages { get; set; }
}

public class Card
{
    public Guid Id { get; set; }
    public string InternalTitle { get; set; }
    public string Title { get; set; }
    public string Subtitle { get; set; }
    public string ExternalId { get; set; }
    public string CardType { get; set; }
    public int Duration { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime PublishedAt { get; set; }
    public string CmsUrl { get; set; }
    public string CmsAnalyticsUrl { get; set; }
    public Category[] Categories { get; set; }
    public Collection[] Collections { get; set; }
}

public class Category
{
    public string Title { get; set; }
    public string ExternalId { get; set; }
}

public class Collection
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string ExternalId { get; set; }
}

// Usage
var client = new CardsClient(Environment.GetEnvironmentVariable("STORYTELLER_API_KEY"));

try
{
    // Get all cards
    var allCards = await client.GetCardsAsync();
    Console.WriteLine($"Found {allCards.Cards.Length} cards");

    // Search for specific cards
    var searchResults = await client.GetCardsAsync("product", "", 1, 20);
    Console.WriteLine($"Found {searchResults.Cards.Length} cards matching 'product'");

    // Filter by external ID
    var filteredCards = await client.GetCardsAsync("", "product-card-001", 1, 20);
    Console.WriteLine($"Found {filteredCards.Cards.Length} cards with external ID 'product-card-001'");

    // Get cards sorted by publish date
    var sortedCards = await client.GetCardsAsync("", "", 1, 50, "PublishedDesc");
    Console.WriteLine($"Retrieved {sortedCards.Cards.Length} cards sorted by publish date");

    // Extract card information
    var cardInfo = allCards.Cards.Select(c => new {
        Id = c.Id,
        Type = c.CardType,
        Title = c.Title
    }).ToArray();
    Console.WriteLine($"Available cards: {string.Join(", ", cardInfo.Select(c => $"{c.Title} ({c.Type})"))}");
}
catch (Exception ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
# Get all cards (first page)
curl -X GET "https://integrations.usestoryteller.com/api/cards" \
  -H "x-storyteller-api-key: your-api-key-here"

# Search for specific cards
curl -X GET "https://integrations.usestoryteller.com/api/cards?searchText=product&pageSize=20" \
  -H "x-storyteller-api-key: your-api-key-here"

# Filter by external ID
curl -X GET "https://integrations.usestoryteller.com/api/cards?externalId=product-card-001" \
  -H "x-storyteller-api-key: your-api-key-here"

# Get cards sorted by publish date
curl -X GET "https://integrations.usestoryteller.com/api/cards?sort=PublishedDesc&pageSize=50" \
  -H "x-storyteller-api-key: your-api-key-here"

Usage with Workflows#

Cards can be referenced in workflow metadata when creating related content:

// 1. Get available cards
const cardsData = await getCards();
const productCards = cardsData.cards.filter(card => card.cardType === 'product');

// 2. Use card ID in workflow metadata
const workflowMetadata = {
  'https://example.com/video.mp4': {
    'Title': 'Product Demo Video',
    'relatedCardId': productCards[0].id, // Reference related card
    'cardType': productCards[0].cardType
  }
};

// 3. Execute workflow with card metadata
await executeWorkflow(['create-product-story'], ['https://example.com/video.mp4'], workflowMetadata);

Filtering by Card Type#

async function getCardsByType(cardType) {
  const allCards = await getAllCards();
  return allCards.filter(card => card.cardType === cardType);
}

// Get specific card types
const productCards = await getCardsByType('product');
const videoCards = await getCardsByType('video');
const infoCards = await getCardsByType('info');

Pagination Example#

async function getAllCards() {
  let allCards = [];
  let currentPage = 1;
  let totalPages = 1;

  do {
    const response = await getCards('', currentPage, 50);
    allCards.push(...response.cards);
    totalPages = response.totalPages;
    currentPage++;
  } while (currentPage <= totalPages);

  return allCards;
}

// Usage
const allCards = await getAllCards();
console.log(`Retrieved ${allCards.length} total cards`);

Error Handling#

Common Error Responses#

{
  "status": 401,
  "type": "https://datatracker.ietf.org/doc/html/rfc7235#section-3.1",
  "title": "Unauthorized",
  "detail": "No valid API key provided",
  "instance": ""
}
{
  "status": 400,
  "type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
  "title": "Bad Request",
  "detail": "Invalid page size. Must be between 1 and 100",
  "instance": ""
}

Best Practices#

  1. Cache Results - Cards don't change frequently, consider caching for short periods
  2. Use Pagination - For tenants with many cards, use appropriate page sizes (max 100)
  3. Search Efficiently - Use search text to filter results when looking for specific cards
  4. Filter by Type - Use card type information to organize content by category
  5. Handle Empty Results - Some tenants may have no cards created
  6. Use Sorting - Sort by publish date or alphabetically for better organization
  7. Track Analytics - Use the cmsAnalyticsUrl to provide direct access to performance data