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#
- Cache Results - Sheets don't change frequently, consider caching for short periods
- Use Pagination - For tenants with many sheets, use appropriate page sizes (max 100)
- Search Efficiently - Use search text to filter results when looking for specific sheet types
- Handle Null External IDs - Some sheets may not have external IDs, handle null values gracefully
- Validate URLs - Check that sheet URLs are accessible before using them in workflows
- Use Sorting - Sort alphabetically for better organization in UI lists
- 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.
Related Documentation#
- Cards API - Get cards that may reference sheets
- Stories API - Get stories that may link to sheets
- Authentication - API key setup and usage
- Executing Workflows - Use sheet URLs in workflow metadata
- Troubleshooting - Common issues and solutions