Skip to content

Sheets API#

The Sheets endpoints allow you to retrieve sheets from your Storyteller tenant. Sheets are informational content elements that can contain forms, links, and other interactive components.

Endpoints#

Get All Sheets#

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

Get Sheet by External ID#

GET https://integrations.usestoryteller.com/api/sheets/{externalId}

Headers#

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

Get All Sheets#

Query Parameters#

Parameter Type Required Default Description
searchText string No - Filter sheets by title
externalId string No - Filter sheets 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

Response#

Success Response (200 OK)#

{
  "sheets": [
    {
      "id": "3e9444d1-1a21-4d3e-cbdc-3a0986ec9838",
      "title": "Contact Us Form",
      "url": "https://yourtenant.usestoryteller.com/sheets/contact-form",
      "externalId": "contact-form-sheet"
    },
    {
      "id": "4f8555e2-2b32-5e4f-dccd-4b1087fd0949",
      "title": "Newsletter Signup",
      "url": "https://yourtenant.usestoryteller.com/sheets/newsletter",
      "externalId": "newsletter-signup"
    }
  ],
  "pageSize": 10,
  "currentPage": 1,
  "totalPages": 1
}

Get Sheet by External ID#

Path Parameters#

Parameter Type Required Description
externalId string Yes The external ID of the sheet

Response (200 OK)#

Returns a single sheet object with the same structure as shown above (without pagination fields).

Response (404 Not Found)#

{
  "status": 404,
  "type": "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
  "title": "Not Found",
  "detail": "Sheet with externalId sheet-001 not found",
  "instance": ""
}

Response Fields#

Sheet Object#

Field Type Description
id string Unique sheet identifier
title string Display title of the sheet
url string Public URL where the sheet can be accessed
externalId string External identifier for the sheet (may be null)

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 getSheets(searchText = '', currentPage = 1, pageSize = 10, sort = '') {
  const params = new URLSearchParams({
    ...(searchText && { searchText }),
    currentPage: currentPage.toString(),
    pageSize: pageSize.toString(),
    ...(sort && { sort })
  });

  try {
    const response = await fetch(`https://integrations.usestoryteller.com/api/sheets?${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 sheets:', error);
    throw error;
  }
}

// Usage examples
async function examples() {
  // Get all sheets
  const allSheets = await getSheets();
  console.log(`Found ${allSheets.sheets.length} sheets`);

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

  // Get sheets sorted alphabetically
  const sortedSheets = await getSheets('', 1, 50, 'AlphabeticalAsc');
  console.log(`Retrieved ${sortedSheets.sheets.length} sheets sorted alphabetically`);

  // Get sheet URLs for linking
  const sheetUrls = allSheets.sheets.map(sheet => ({
    title: sheet.title,
    url: sheet.url,
    externalId: sheet.externalId
  }));
  console.log('Available sheet URLs:', sheetUrls);
}
import requests
import os
from urllib.parse import urlencode

def get_sheets(search_text='', current_page=1, page_size=10, sort=''):
    url = 'https://integrations.usestoryteller.com/api/sheets'
    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 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 sheets: {e}')
        if hasattr(e.response, 'json'):
            print(f'Error details: {e.response.json()}')
        raise

# Usage examples
try:
    # Get all sheets
    all_sheets = get_sheets()
    print(f'Found {len(all_sheets["sheets"])} sheets')

    # Search for specific sheets
    search_results = get_sheets(search_text='form', page_size=20)
    print(f'Found {len(search_results["sheets"])} sheets matching "form"')

    # Get sheets sorted alphabetically
    sorted_sheets = get_sheets(page_size=50, sort='AlphabeticalAsc')
    print(f'Retrieved {len(sorted_sheets["sheets"])} sheets sorted alphabetically')

    # Extract sheet URLs for linking
    sheet_urls = [{
        'title': sheet['title'],
        'url': sheet['url'],
        'externalId': sheet.get('externalId')
    } for sheet in all_sheets['sheets']]
    print('Available sheet URLs:', sheet_urls)

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

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

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

    public async Task<SheetsResponse> GetSheetsAsync(string searchText = "", 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(sort))
            {
                queryParams.Add($"sort={sort}");
            }

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

            var responseContent = await response.Content.ReadAsStringAsync();
            var sheets = JsonConvert.DeserializeObject<SheetsResponse>(responseContent);

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

public class SheetsResponse
{
    public Sheet[] Sheets { get; set; }
    public int PageSize { get; set; }
    public int CurrentPage { get; set; }
    public int TotalPages { get; set; }
}

public class Sheet
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string Url { get; set; }
    public string ExternalId { get; set; }
}

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

try
{
    // Get all sheets
    var allSheets = await client.GetSheetsAsync();
    Console.WriteLine($"Found {allSheets.Sheets.Length} sheets");

    // Search for specific sheets
    var searchResults = await client.GetSheetsAsync("form", 1, 20);
    Console.WriteLine($"Found {searchResults.Sheets.Length} sheets matching 'form'");

    // Get sheets sorted alphabetically
    var sortedSheets = await client.GetSheetsAsync("", 1, 50, "AlphabeticalAsc");
    Console.WriteLine($"Retrieved {sortedSheets.Sheets.Length} sheets sorted alphabetically");

    // Extract sheet URLs for linking
    var sheetUrls = allSheets.Sheets.Select(s => new {
        Title = s.Title,
        Url = s.Url,
        ExternalId = s.ExternalId
    }).ToArray();
    Console.WriteLine($"Available sheet URLs: {string.Join(", ", sheetUrls.Select(s => $"{s.Title} ({s.Url})"))}");
}
catch (Exception ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
# Get all sheets (first page)
curl -X GET "https://integrations.usestoryteller.com/api/sheets" \
  -H "x-storyteller-api-key: your-api-key-here"

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

# Get sheets sorted alphabetically
curl -X GET "https://integrations.usestoryteller.com/api/sheets?sort=AlphabeticalAsc&pageSize=50" \
  -H "x-storyteller-api-key: your-api-key-here"

# Get second page of sheets
curl -X GET "https://integrations.usestoryteller.com/api/sheets?currentPage=2&pageSize=10" \
  -H "x-storyteller-api-key: your-api-key-here"

Usage with Workflows#

Sheets can be referenced in workflow metadata when creating interactive content:

// 1. Get available sheets
const sheetsData = await getSheets();
const contactForms = sheetsData.sheets.filter(sheet =>
  sheet.title.toLowerCase().includes('contact') ||
  sheet.title.toLowerCase().includes('form')
);

// 2. Use sheet URL in workflow metadata
const workflowMetadata = {
  'https://example.com/video.mp4': {
    'Title': 'Contact Us Video',
    'callToActionUrl': contactForms[0]?.url, // Link to contact form
    'sheetId': contactForms[0]?.externalId
  }
};

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

Finding Sheets by Type#

async function findSheetsByType(searchTerm) {
  const sheetsData = await getSheets(searchTerm);
  return sheetsData.sheets;
}

// Find specific types of sheets
const contactForms = await findSheetsByType('contact');
const newsletters = await findSheetsByType('newsletter');
const surveys = await findSheetsByType('survey');

Pagination Example#

async function getAllSheets() {
  let allSheets = [];
  let currentPage = 1;
  let totalPages = 1;

  do {
    const response = await getSheets('', currentPage, 50);
    allSheets.push(...response.sheets);
    totalPages = response.totalPages;
    currentPage++;
  } while (currentPage <= totalPages);

  return allSheets;
}

// Usage
const allSheets = await getAllSheets();
console.log(`Retrieved ${allSheets.length} total sheets`);

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 - Sheets don't change frequently, consider caching for short periods
  2. Use Pagination - For tenants with many sheets, use appropriate page sizes (max 100)
  3. Search Efficiently - Use search text to filter results when looking for specific sheet types
  4. Handle Null External IDs - Some sheets may not have external IDs, handle null values gracefully
  5. Validate URLs - Check that sheet URLs are accessible before using them in workflows
  6. Use Sorting - Sort alphabetically for better organization in UI lists
  7. Link Appropriately - Use sheet URLs as call-to-action links in your content

Common Use Cases#

Contact Forms#

Use sheets that contain contact forms to capture user information from your content.

Newsletter Signups#

Link to newsletter signup sheets to grow your subscriber base.

Surveys and Feedback#

Direct users to survey sheets to collect feedback and insights.

Product Information#

Use informational sheets to provide detailed product specifications or documentation.