Skip to content

Getting Workflows#

The GET /api/workflows endpoint allows you to retrieve the list of available workflows for your Storyteller account. This is typically the first API call you'll make when integrating with Storyteller.

Endpoint Details#

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

Headers#

Header Required Description
x-storyteller-api-key Yes Your API key for authentication
Content-Type Yes application/json

Response#

The endpoint returns a JSON object containing an array of available workflows:

{
  "workflows": [
    {
      "id": "add-media",
      "title": "Add to Media Library",
      "requiredMetadata": [
        {
          "name": "Title",
          "fieldLabel": "Content Title",
          "type": "string",
          "remarks": "The title or name of the content"
        }
      ],
      "optionalMetadata": [
        {
          "name": "Description",
          "fieldLabel": "Content Description",
          "type": "string",
          "remarks": "Optional description of the content"
        },
        {
          "name": "Tags",
          "fieldLabel": "Content Tags",
          "type": "string",
          "remarks": "Comma-separated tags for categorization"
        }
      ]
    },
    {
      "id": "add-clip",
      "title": "Add Clip",
      "requiredMetadata": [
        {
          "name": "Title",
          "fieldLabel": "Clip Title",
          "type": "string"
        }
      ],
      "optionalMetadata": []
    },
    {
      "id": "create-story",
      "title": "Create Story",
      "requiredMetadata": [
        {
          "name": "Title",
          "fieldLabel": "Story Title",
          "type": "string"
        }
      ],
      "optionalMetadata": [
        {
          "name": "Description",
          "fieldLabel": "Story Description",
          "type": "string",
          "remarks": "Brief description of the story content"
        }
      ]
    }
  ]
}

Code Examples#

curl -X GET "https://integrations.usestoryteller.com/api/workflows" \
  -H "x-storyteller-api-key: your-api-key-here" \
  -H "Content-Type: application/json"
const fetch = require('node-fetch');

async function getWorkflows() {
  try {
    const response = await fetch('https://integrations.usestoryteller.com/api/workflows', {
      method: 'GET',
      headers: {
        'x-storyteller-api-key': process.env.STORYTELLER_API_KEY,
        'Content-Type': 'application/json'
      }
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

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

// Usage
getWorkflows()
  .then(workflows => {
    console.log('Available workflows:', workflows.length);
    workflows.forEach(workflow => {
      console.log(`\nšŸ“‹ ${workflow.name} (${workflow.id})`);
      console.log(`   ${workflow.description}`);

      if (workflow.requiredMetadata && workflow.requiredMetadata.length > 0) {
        console.log('   Required metadata:');
        workflow.requiredMetadata.forEach(meta => {
          console.log(`   - ${meta.key}: ${meta.description}`);
        });
      }

      if (workflow.optionalMetadata && workflow.optionalMetadata.length > 0) {
        console.log('   Optional metadata:');
        workflow.optionalMetadata.forEach(meta => {
          console.log(`   - ${meta.key}: ${meta.description}`);
        });
      }
    });
  })
  .catch(error => console.error('Failed to get workflows:', error));
import requests
import os
from typing import List, Dict, Optional

def get_workflows() -> List[Dict]:
    """Fetch available workflows from the Storyteller Integration API."""
    url = 'https://integrations.usestoryteller.com/api/workflows'
    headers = {
        'x-storyteller-api-key': os.environ.get('STORYTELLER_API_KEY'),
        'Content-Type': 'application/json'
    }

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

        data = response.json()
        return data.get('workflows', [])
    except requests.exceptions.RequestException as e:
        print(f'Error fetching workflows: {e}')
        raise

def display_workflow_info(workflows: List[Dict]) -> None:
    """Display formatted workflow information."""
    print(f'Available workflows: {len(workflows)}')

    for workflow in workflows:
        print(f'\nšŸ“‹ {workflow["name"]} ({workflow["id"]})')
        print(f'   {workflow["description"]}')

        if workflow.get('requiredMetadata'):
            print('   Required metadata:')
            for meta in workflow['requiredMetadata']:
                print(f'   - {meta["key"]}: {meta["description"]}')

        if workflow.get('optionalMetadata'):
            print('   Optional metadata:')
            for meta in workflow['optionalMetadata']:
                print(f'   - {meta["key"]}: {meta["description"]}')

# Usage
if __name__ == '__main__':
    try:
        workflows = get_workflows()
        display_workflow_info(workflows)
    except Exception as e:
        print(f'Failed to get workflows: {e}')
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using Newtonsoft.Json;

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

    public WorkflowsClient(string apiKey)
    {
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("x-storyteller-api-key", apiKey);
        _httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
    }

    public async Task<WorkflowsResponse> GetWorkflowsAsync()
    {
        try
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/api/workflows");
            response.EnsureSuccessStatusCode();

            var content = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<WorkflowsResponse>(content);
        }
        catch (HttpRequestException ex)
        {
            throw new Exception($"Error fetching workflows: {ex.Message}", ex);
        }
    }

    public void DisplayWorkflowInfo(WorkflowsResponse response)
    {
        Console.WriteLine($"Available workflows: {response.Workflows.Length}");

        foreach (var workflow in response.Workflows)
        {
            Console.WriteLine($"\nšŸ“‹ {workflow.Name} ({workflow.Id})");
            Console.WriteLine($"   {workflow.Description}");

            if (workflow.RequiredMetadata != null && workflow.RequiredMetadata.Length > 0)
            {
                Console.WriteLine("   Required metadata:");
                foreach (var meta in workflow.RequiredMetadata)
                {
                    Console.WriteLine($"   - {meta.Key}: {meta.Description}");
                }
            }

            if (workflow.OptionalMetadata != null && workflow.OptionalMetadata.Length > 0)
            {
                Console.WriteLine("   Optional metadata:");
                foreach (var meta in workflow.OptionalMetadata)
                {
                    Console.WriteLine($"   - {meta.Key}: {meta.Description}");
                }
            }
        }
    }

    public void Dispose()
    {
        _httpClient?.Dispose();
    }
}

public class WorkflowsResponse
{
    [JsonProperty("workflows")]
    public Workflow[] Workflows { get; set; }
}

public class Workflow
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("description")]
    public string Description { get; set; }

    [JsonProperty("requiredMetadata")]
    public MetadataField[] RequiredMetadata { get; set; }

    [JsonProperty("optionalMetadata")]
    public MetadataField[] OptionalMetadata { get; set; }
}

public class MetadataField
{
    [JsonProperty("key")]
    public string Key { get; set; }

    [JsonProperty("fieldLabel")]
    public string FieldLabel { get; set; }

    [JsonProperty("description")]
    public string Description { get; set; }
}

// Usage
public class Program
{
    public static async Task Main(string[] args)
    {
        using var client = new WorkflowsClient(Environment.GetEnvironmentVariable("STORYTELLER_API_KEY"));

        try
        {
            var result = await client.GetWorkflowsAsync();
            client.DisplayWorkflowInfo(result);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

Understanding the Response#

Workflow Object#

Each workflow in the response contains:

Field Type Description
id string Unique identifier for the workflow
name string Human-readable name of the workflow
description string Detailed description of what the workflow does
requiredMetadata array Metadata fields that must be provided
optionalMetadata array Metadata fields that can optionally be provided

Metadata Fields#

Both requiredMetadata and optionalMetadata contain arrays of metadata field objects:

Field Type Description
key string The metadata key to use in your execute request
fieldLabel string Human-readable label for UI display
description string Explanation of what this metadata field is used for

Using Workflow Information#

Once you have the list of workflows, you can:

  1. Present Options to Users - Use the name and description to show workflow options in your UI
  2. Build Metadata Forms - Use requiredMetadata and optionalMetadata to create forms
  3. Validate Requests - Ensure required metadata is provided before executing workflows
  4. Cache Results - Store workflow information to avoid repeated API calls

Example: Building a Metadata Form#

function buildMetadataForm(workflow) {
  const form = document.createElement('form');
  form.className = 'workflow-form';

  // Add workflow information
  const workflowInfo = document.createElement('div');
  workflowInfo.className = 'workflow-info';
  workflowInfo.innerHTML = `
    <h3>${workflow.name}</h3>
    <p>${workflow.description}</p>
  `;
  form.appendChild(workflowInfo);

  // Add required fields
  if (workflow.requiredMetadata && workflow.requiredMetadata.length > 0) {
    const requiredSection = document.createElement('div');
    requiredSection.className = 'required-fields';
    requiredSection.innerHTML = '<h4>Required Information</h4>';

    workflow.requiredMetadata.forEach(field => {
      const fieldDiv = document.createElement('div');
      fieldDiv.className = 'form-field required';
      fieldDiv.innerHTML = `
        <label for="${field.key}" class="required">
          ${field.fieldLabel} *
        </label>
        <input type="text" id="${field.key}" name="${field.key}" required />
        <small class="field-description">${field.description}</small>
      `;
      requiredSection.appendChild(fieldDiv);
    });

    form.appendChild(requiredSection);
  }

  // Add optional fields
  if (workflow.optionalMetadata && workflow.optionalMetadata.length > 0) {
    const optionalSection = document.createElement('div');
    optionalSection.className = 'optional-fields';
    optionalSection.innerHTML = '<h4>Optional Information</h4>';

    workflow.optionalMetadata.forEach(field => {
      const fieldDiv = document.createElement('div');
      fieldDiv.className = 'form-field optional';
      fieldDiv.innerHTML = `
        <label for="${field.key}">
          ${field.fieldLabel}
        </label>
        <input type="text" id="${field.key}" name="${field.key}" />
        <small class="field-description">${field.description}</small>
      `;
      optionalSection.appendChild(fieldDiv);
    });

    form.appendChild(optionalSection);
  }

  // Add submit button
  const submitButton = document.createElement('button');
  submitButton.type = 'submit';
  submitButton.textContent = `Execute ${workflow.name}`;
  submitButton.className = 'submit-button';
  form.appendChild(submitButton);

  return form;
}

// Usage example
getWorkflows()
  .then(workflows => {
    const workflowSelect = document.getElementById('workflow-select');
    const formContainer = document.getElementById('form-container');

    // Populate workflow selector
    workflows.forEach(workflow => {
      const option = document.createElement('option');
      option.value = workflow.id;
      option.textContent = workflow.name;
      workflowSelect.appendChild(option);
    });

    // Handle workflow selection
    workflowSelect.addEventListener('change', (e) => {
      const selectedWorkflow = workflows.find(w => w.id === e.target.value);
      if (selectedWorkflow) {
        formContainer.innerHTML = '';
        formContainer.appendChild(buildMetadataForm(selectedWorkflow));
      }
    });
  });
def build_metadata_form_data(workflow: Dict) -> Dict:
    """Build form configuration data for a workflow."""
    form_config = {
        'workflow_id': workflow['id'],
        'workflow_name': workflow['name'],
        'workflow_description': workflow['description'],
        'required_fields': [],
        'optional_fields': []
    }

    # Process required metadata
    if workflow.get('requiredMetadata'):
        for field in workflow['requiredMetadata']:
            form_config['required_fields'].append({
                'key': field['key'],
                'label': field['fieldLabel'],
                'description': field['description'],
                'required': True,
                'type': 'text'  # Could be enhanced to support different field types
            })

    # Process optional metadata
    if workflow.get('optionalMetadata'):
        for field in workflow['optionalMetadata']:
            form_config['optional_fields'].append({
                'key': field['key'],
                'label': field['fieldLabel'],
                'description': field['description'],
                'required': False,
                'type': 'text'
            })

    return form_config

def generate_html_form(form_config: Dict) -> str:
    """Generate HTML form from workflow configuration."""
    html = f"""
    <form class="workflow-form" data-workflow-id="{form_config['workflow_id']}">
        <div class="workflow-info">
            <h3>{form_config['workflow_name']}</h3>
            <p>{form_config['workflow_description']}</p>
        </div>
    """

    # Add required fields
    if form_config['required_fields']:
        html += '<div class="required-fields"><h4>Required Information</h4>'
        for field in form_config['required_fields']:
            html += f"""
            <div class="form-field required">
                <label for="{field['key']}" class="required">
                    {field['label']} *
                </label>
                <input type="{field['type']}" id="{field['key']}" name="{field['key']}" required />
                <small class="field-description">{field['description']}</small>
            </div>
            """
        html += '</div>'

    # Add optional fields
    if form_config['optional_fields']:
        html += '<div class="optional-fields"><h4>Optional Information</h4>'
        for field in form_config['optional_fields']:
            html += f"""
            <div class="form-field optional">
                <label for="{field['key']}">
                    {field['label']}
                </label>
                <input type="{field['type']}" id="{field['key']}" name="{field['key']}" />
                <small class="field-description">{field['description']}</small>
            </div>
            """
        html += '</div>'

    html += f"""
        <button type="submit" class="submit-button">
            Execute {form_config['workflow_name']}
        </button>
    </form>
    """

    return html

# Usage example
try:
    workflows = get_workflows()
    for workflow in workflows:
        form_config = build_metadata_form_data(workflow)
        html_form = generate_html_form(form_config)
        print(f"Form HTML for {workflow['name']}:")
        print(html_form)
        print("-" * 50)
except Exception as e:
    print(f"Error generating forms: {e}")
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;

public class MetadataFormBuilder
{
    public class FormField
    {
        public string Key { get; set; }
        public string Label { get; set; }
        public string Description { get; set; }
        public bool Required { get; set; }
        public string Type { get; set; } = "text";
    }

    public class FormConfiguration
    {
        public string WorkflowId { get; set; }
        public string WorkflowName { get; set; }
        public string WorkflowDescription { get; set; }
        public List<FormField> RequiredFields { get; set; } = new List<FormField>();
        public List<FormField> OptionalFields { get; set; } = new List<FormField>();
    }

    public FormConfiguration BuildFormConfiguration(Workflow workflow)
    {
        var config = new FormConfiguration
        {
            WorkflowId = workflow.Id,
            WorkflowName = workflow.Name,
            WorkflowDescription = workflow.Description
        };

        // Process required metadata
        if (workflow.RequiredMetadata != null)
        {
            config.RequiredFields = workflow.RequiredMetadata.Select(field => new FormField
            {
                Key = field.Key,
                Label = field.FieldLabel,
                Description = field.Description,
                Required = true,
                Type = "text"
            }).ToList();
        }

        // Process optional metadata
        if (workflow.OptionalMetadata != null)
        {
            config.OptionalFields = workflow.OptionalMetadata.Select(field => new FormField
            {
                Key = field.Key,
                Label = field.FieldLabel,
                Description = field.Description,
                Required = false,
                Type = "text"
            }).ToList();
        }

        return config;
    }

    public string GenerateHtmlForm(FormConfiguration config)
    {
        var html = new StringBuilder();

        html.AppendLine($@"<form class=""workflow-form"" data-workflow-id=""{config.WorkflowId}"">");
        html.AppendLine($@"    <div class=""workflow-info"">");
        html.AppendLine($@"        <h3>{config.WorkflowName}</h3>");
        html.AppendLine($@"        <p>{config.WorkflowDescription}</p>");
        html.AppendLine($@"    </div>");

        // Add required fields
        if (config.RequiredFields.Any())
        {
            html.AppendLine($@"    <div class=""required-fields"">");
            html.AppendLine($@"        <h4>Required Information</h4>");

            foreach (var field in config.RequiredFields)
            {
                html.AppendLine($@"        <div class=""form-field required"">");
                html.AppendLine($@"            <label for=""{field.Key}"" class=""required"">");
                html.AppendLine($@"                {field.Label} *");
                html.AppendLine($@"            </label>");
                html.AppendLine($@"            <input type=""{field.Type}"" id=""{field.Key}"" name=""{field.Key}"" required />");
                html.AppendLine($@"            <small class=""field-description"">{field.Description}</small>");
                html.AppendLine($@"        </div>");
            }

            html.AppendLine($@"    </div>");
        }

        // Add optional fields
        if (config.OptionalFields.Any())
        {
            html.AppendLine($@"    <div class=""optional-fields"">");
            html.AppendLine($@"        <h4>Optional Information</h4>");

            foreach (var field in config.OptionalFields)
            {
                html.AppendLine($@"        <div class=""form-field optional"">");
                html.AppendLine($@"            <label for=""{field.Key}"">");
                html.AppendLine($@"                {field.Label}");
                html.AppendLine($@"            </label>");
                html.AppendLine($@"            <input type=""{field.Type}"" id=""{field.Key}"" name=""{field.Key}"" />");
                html.AppendLine($@"            <small class=""field-description"">{field.Description}</small>");
                html.AppendLine($@"        </div>");
            }

            html.AppendLine($@"    </div>");
        }

        html.AppendLine($@"    <button type=""submit"" class=""submit-button"">");
        html.AppendLine($@"        Execute {config.WorkflowName}");
        html.AppendLine($@"    </button>");
        html.AppendLine($@"</form>");

        return html.ToString();
    }
}

// Usage example
public class Program
{
    public static async Task GenerateFormsExample()
    {
        using var client = new WorkflowsClient(Environment.GetEnvironmentVariable("STORYTELLER_API_KEY"));
        var formBuilder = new MetadataFormBuilder();

        try
        {
            var response = await client.GetWorkflowsAsync();

            foreach (var workflow in response.Workflows)
            {
                var formConfig = formBuilder.BuildFormConfiguration(workflow);
                var htmlForm = formBuilder.GenerateHtmlForm(formConfig);

                Console.WriteLine($"Form HTML for {workflow.Name}:");
                Console.WriteLine(htmlForm);
                Console.WriteLine(new string('-', 50));
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error generating forms: {ex.Message}");
        }
    }
}

Response Status Codes#

Status Code Description
200 OK Successfully retrieved workflows
401 Unauthorized Invalid or missing API key
403 Forbidden API key doesn't have access to workflows
500 Internal Server Error Server error occurred

Error Handling#

Common Errors#

{
  "error": "Unauthorized",
  "message": "Invalid API key",
  "statusCode": 401
}

Best Practices#

  • Always check the response status before processing
  • Implement retry logic for temporary failures
  • Cache workflow results to reduce API calls
  • Validate your API key if you get 401 errors

Next Steps#

Once you have your available workflows:

  1. Choose the appropriate workflow for your content
  2. Gather the required metadata
  3. Execute the workflow using Executing Workflows