Skip to content

Quick Start Guide#

Get started with the Storyteller Integration API in minutes. This guide will walk you through your first successful workflow execution.

Prerequisites#

Before you begin, ensure you have:

  • API Key - Contact [email protected] or get one from the Storyteller CMS
  • Publicly accessible media URL - A video or image hosted online
  • Development environment - Your preferred programming language setup

Step 1: Test Your API Key#

First, verify your API key works by fetching available workflows:

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

async function testConnection() {
  const response = await fetch('https://integrations.usestoryteller.com/api/workflows', {
    headers: {
      'x-storyteller-api-key': 'YOUR_API_KEY_HERE'
    }
  });

  if (response.ok) {
    const workflows = await response.json();
    console.log('āœ… Connected! Available workflows:', workflows.map(w => w.code));
    return workflows;
  } else {
    console.error('āŒ Connection failed:', await response.text());
  }
}

testConnection();
import requests

def test_connection():
    response = requests.get(
        'https://integrations.usestoryteller.com/api/workflows',
        headers={'x-storyteller-api-key': 'YOUR_API_KEY_HERE'}
    )

    if response.ok:
        workflows = response.json()
        print('āœ… Connected! Available workflows:', [w['code'] for w in workflows])
        return workflows
    else:
        print('āŒ Connection failed:', response.text)

test_connection()
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class ConnectionTester
{
    private readonly HttpClient _httpClient;

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

    public async Task TestConnectionAsync()
    {
        try
        {
            var response = await _httpClient.GetAsync("https://integrations.usestoryteller.com/api/workflows");

            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var workflows = JsonConvert.DeserializeObject<dynamic>(json);
                Console.WriteLine("āœ… Connected! Available workflows: " + workflows);
            }
            else
            {
                var error = await response.Content.ReadAsStringAsync();
                Console.WriteLine("āŒ Connection failed: " + error);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"āŒ Request failed: {ex.Message}");
        }
    }
}

// Usage
var tester = new ConnectionTester("YOUR_API_KEY_HERE");
await tester.TestConnectionAsync();

Expected Output:

āœ… Connected! Available workflows: ['add-media', 'add-clip', 'add-to-story']

Step 2: Prepare Your Media#

You need a publicly accessible URL to a video or image file. For testing, you can use this sample video:

https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4

Media Requirements: - Must be accessible over HTTPS - Supported formats: MP4, MOV, AVI (video), JPG, PNG (images) - Must return proper HTTP status codes - Maximum file size depends on your account tier

Validate Your Media URL#

curl -I "https://your-domain.com/your-video.mp4"

Look for 200 OK status and appropriate Content-Type headers.

Step 3: Your First Workflow Execution#

Let's execute a simple workflow to add media to your Storyteller tenant:

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

async function executeFirstWorkflow() {
  const payload = {
    workflows: ['add-media'],  // Use a workflow from step 1
    mediaUrls: ['https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4'],
    metadata: {
      'https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4': {
        'Title': 'My First Storyteller Video'
      }
    }
  };

  try {
    const response = await fetch('https://integrations.usestoryteller.com/api/workflows/execute', {
      method: 'POST',
      headers: {
        'x-storyteller-api-key': 'YOUR_API_KEY_HERE',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload)
    });

    if (response.ok) {
      const result = await response.json();
      console.log('āœ… Workflow started!');
      console.log('Correlation ID:', result.correlationId);
      return result.correlationId;
    } else {
      const error = await response.json();
      console.error('āŒ Workflow failed:', error);
    }
  } catch (error) {
    console.error('āŒ Request failed:', error);
  }
}

executeFirstWorkflow();
import requests
import json

def execute_first_workflow():
    payload = {
        'workflows': ['add-media'],  # Use a workflow from step 1
        'mediaUrls': ['https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4'],
        'metadata': {
            'https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4': {
                'Title': 'My First Storyteller Video'
            }
        }
    }

    try:
        response = requests.post(
            'https://integrations.usestoryteller.com/api/workflows/execute',
            headers={
                'x-storyteller-api-key': 'YOUR_API_KEY_HERE',
                'Content-Type': 'application/json'
            },
            json=payload
        )

        if response.ok:
            result = response.json()
            print('āœ… Workflow started!')
            print('Correlation ID:', result['correlationId'])
            return result['correlationId']
        else:
            error = response.json()
            print('āŒ Workflow failed:', error)
    except Exception as e:
        print('āŒ Request failed:', e)

execute_first_workflow()
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class WorkflowExecutor
{
    private readonly HttpClient _httpClient;

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

    public async Task<string> ExecuteFirstWorkflowAsync()
    {
        var payload = new
        {
            workflows = new[] { "add-media" },
            mediaUrls = new[] { "https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4" },
            metadata = new
            {
                [
                    "https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4"
                ] = new { Title = "My First Storyteller Video" }
            }
        };

        try
        {
            var json = JsonConvert.SerializeObject(payload);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync(
                "https://integrations.usestoryteller.com/api/workflows/execute", 
                content
            );

            if (response.IsSuccessStatusCode)
            {
                var result = await response.Content.ReadAsStringAsync();
                var data = JsonConvert.DeserializeObject<dynamic>(result);
                Console.WriteLine("āœ… Workflow started!");
                Console.WriteLine($"Correlation ID: {data.correlationId}");
                return data.correlationId;
            }
            else
            {
                var error = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"āŒ Workflow failed: {error}");
                return null;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"āŒ Request failed: {ex.Message}");
            return null;
        }
    }
}

// Usage
var executor = new WorkflowExecutor("YOUR_API_KEY_HERE");
var correlationId = await executor.ExecuteFirstWorkflowAsync();
curl -X POST "https://integrations.usestoryteller.com/api/workflows/execute" \
  -H "x-storyteller-api-key: YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "workflows": ["add-media"],
    "mediaUrls": ["https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4"],
    "metadata": {
      "https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4": {
        "Title": "My First Storyteller Video"
      }
    }
  }'

Expected Output:

{
  "correlationId": "12345678-1234-1234-1234-123456789012",
  "message": "Workflow execution started",
  "status": "processing"
}

Step 4: Check Workflow Status#

Use the correlation ID from step 3 to check the workflow progress:

async function checkWorkflowStatus(correlationId) {
  try {
    const response = await fetch(`https://integrations.usestoryteller.com/api/${correlationId}/status`, {
      headers: {
        'x-storyteller-api-key': 'YOUR_API_KEY_HERE'
      }
    });

    if (response.ok) {
      const status = await response.json();
      console.log('šŸ“Š Workflow Status:');

      status.forEach(workflow => {
        console.log(`  ${workflow.code}: ${workflow.status}`);
        console.log(`  Message: ${workflow.message}`);

        workflow.steps.forEach(step => {
          console.log(`    ${step.name}: ${step.status}`);
          if (step.locations) {
            console.log(`    šŸ”— View in Storyteller:`, step.locations[0]);
          }
        });
      });

      return status;
    } else {
      console.error('āŒ Status check failed:', await response.text());
    }
  } catch (error) {
    console.error('āŒ Request failed:', error);
  }
}

// Use the correlation ID from step 3
checkWorkflowStatus('12345678-1234-1234-1234-123456789012');
def check_workflow_status(correlation_id):
    try:
        response = requests.get(
            f'https://integrations.usestoryteller.com/api/{correlation_id}/status',
            headers={'x-storyteller-api-key': 'YOUR_API_KEY_HERE'}
        )

        if response.ok:
            status = response.json()
            print('šŸ“Š Workflow Status:')

            for workflow in status:
                print(f'  {workflow["code"]}: {workflow["status"]}')
                print(f'  Message: {workflow["message"]}')

                for step in workflow['steps']:
                    print(f'    {step["name"]}: {step["status"]}')
                    if 'locations' in step:
                        print(f'    šŸ”— View in Storyteller: {step["locations"][0]}')

            return status
        else:
            print('āŒ Status check failed:', response.text)
    except Exception as e:
        print('āŒ Request failed:', e)

# Use the correlation ID from step 3
check_workflow_status('12345678-1234-1234-1234-123456789012')
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class StatusChecker
{
    private readonly HttpClient _httpClient;

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

    public async Task CheckWorkflowStatusAsync(string correlationId)
    {
        try
        {
            var response = await _httpClient.GetAsync(
                $"https://integrations.usestoryteller.com/api/{correlationId}/status"
            );

            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var status = JsonConvert.DeserializeObject<dynamic[]>(json);
                Console.WriteLine("šŸ“Š Workflow Status:");

                foreach (var workflow in status)
                {
                    Console.WriteLine($"  {workflow.code}: {workflow.status}");
                    Console.WriteLine($"  Message: {workflow.message}");

                    foreach (var step in workflow.steps)
                    {
                        Console.WriteLine($"    {step.name}: {step.status}");
                        if (step.locations != null && step.locations.Count > 0)
                        {
                            Console.WriteLine($"    šŸ”— View in Storyteller: {step.locations[0]}");
                        }
                    }
                }
            }
            else
            {
                var error = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"āŒ Status check failed: {error}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"āŒ Request failed: {ex.Message}");
        }
    }
}

// Usage
var checker = new StatusChecker("YOUR_API_KEY_HERE");
await checker.CheckWorkflowStatusAsync("12345678-1234-1234-1234-123456789012");
# Check workflow status
curl -X GET "https://integrations.usestoryteller.com/api/12345678-1234-1234-1234-123456789012/status" \
  -H "x-storyteller-api-key: YOUR_API_KEY_HERE"

# Pretty print JSON (if you have jq installed)
curl -X GET "https://integrations.usestoryteller.com/api/12345678-1234-1234-1234-123456789012/status" \
  -H "x-storyteller-api-key: YOUR_API_KEY_HERE" | jq '.'

Expected Output:

šŸ“Š Workflow Status:
  add-media: Finished
  Message: Workflow successfully completed
    Create Media Asset: Finished
    šŸ”— View in Storyteller: https://yourtenant.usestoryteller.com/assets/...

Step 5: Complete Example#

Here's a complete example that combines all the steps:

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

class StorytellerClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://integrations.usestoryteller.com';
  }

  async request(endpoint, options = {}) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      ...options,
      headers: {
        'x-storyteller-api-key': this.apiKey,
        'Content-Type': 'application/json',
        ...options.headers
      }
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`API Error: ${error.message || response.statusText}`);
    }

    return response.json();
  }

  async getWorkflows() {
    return this.request('/api/workflows');
  }

  async executeWorkflow(workflows, mediaUrls, metadata, webhooks = {}) {
    return this.request('/api/workflows/execute', {
      method: 'POST',
      body: JSON.stringify({ workflows, mediaUrls, metadata, ...webhooks })
    });
  }

  async getWorkflowStatus(correlationId) {
    return this.request(`/api/${correlationId}/status`);
  }

  async waitForCompletion(correlationId, maxAttempts = 30, intervalMs = 5000) {
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      const status = await this.getWorkflowStatus(correlationId);

      const allComplete = status.every(workflow => 
        workflow.status === 'Finished' || workflow.status === 'Failed'
      );

      if (allComplete) {
        return status;
      }

      console.log(`ā³ Attempt ${attempt}: Still processing...`);
      if (attempt < maxAttempts) {
        await new Promise(resolve => setTimeout(resolve, intervalMs));
      }
    }

    throw new Error('Workflow did not complete within the timeout period');
  }
}

// Usage
async function quickStartExample() {
  const client = new StorytellerClient('YOUR_API_KEY_HERE');

  try {
    // 1. Get available workflows
    console.log('šŸ” Getting available workflows...');
    const workflows = await client.getWorkflows();
    console.log('āœ… Available workflows:', workflows.map(w => w.code));

    // 2. Execute a workflow
    console.log('\nšŸš€ Executing workflow...');
    const result = await client.executeWorkflow(
      ['add-media'],
      ['https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4'],
      {
        'https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4': {
          'Title': 'Quick Start Example Video'
        }
      }
    );
    console.log('āœ… Workflow started! Correlation ID:', result.correlationId);

    // 3. Wait for completion
    console.log('\nā³ Waiting for workflow to complete...');
    const finalStatus = await client.waitForCompletion(result.correlationId);

    console.log('\nšŸŽ‰ Workflow completed!');
    finalStatus.forEach(workflow => {
      console.log(`šŸ“Š ${workflow.code}: ${workflow.status}`);
      workflow.steps.forEach(step => {
        if (step.locations) {
          console.log(`šŸ”— View result: ${step.locations[0]}`);
        }
      });
    });

  } catch (error) {
    console.error('āŒ Error:', error.message);
  }
}

quickStartExample();
import requests
import time
import json

class StorytellerClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = 'https://integrations.usestoryteller.com'
        self.headers = {
            'x-storyteller-api-key': api_key,
            'Content-Type': 'application/json'
        }

    def request(self, endpoint, method='GET', data=None):
        response = requests.request(
            method=method,
            url=f'{self.base_url}{endpoint}',
            headers=self.headers,
            json=data
        )

        if not response.ok:
            try:
                error = response.json()
                raise Exception(f"API Error: {error.get('message', response.text)}")
            except ValueError:
                raise Exception(f"API Error: {response.text}")

        return response.json()

    def get_workflows(self):
        return self.request('/api/workflows')

    def execute_workflow(self, workflows, media_urls, metadata, **webhooks):
        payload = {
            'workflows': workflows,
            'mediaUrls': media_urls,
            'metadata': metadata,
            **webhooks
        }
        return self.request('/api/workflows/execute', 'POST', payload)

    def get_workflow_status(self, correlation_id):
        return self.request(f'/api/{correlation_id}/status')

    def wait_for_completion(self, correlation_id, max_attempts=30, interval_seconds=5):
        for attempt in range(1, max_attempts + 1):
            status = self.get_workflow_status(correlation_id)

            all_complete = all(
                workflow['status'] in ['Finished', 'Failed'] 
                for workflow in status
            )

            if all_complete:
                return status

            print(f'ā³ Attempt {attempt}: Still processing...')
            if attempt < max_attempts:
                time.sleep(interval_seconds)

        raise Exception('Workflow did not complete within the timeout period')

# Usage
def quick_start_example():
    client = StorytellerClient('YOUR_API_KEY_HERE')

    try:
        # 1. Get available workflows
        print('šŸ” Getting available workflows...')
        workflows = client.get_workflows()
        print('āœ… Available workflows:', [w['code'] for w in workflows])

        # 2. Execute a workflow
        print('\nšŸš€ Executing workflow...')
        result = client.execute_workflow(
            ['add-media'],
            ['https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4'],
            {
                'https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4': {
                    'Title': 'Quick Start Example Video'
                }
            }
        )
        print('āœ… Workflow started! Correlation ID:', result['correlationId'])

        # 3. Wait for completion
        print('\nā³ Waiting for workflow to complete...')
        final_status = client.wait_for_completion(result['correlationId'])

        print('\nšŸŽ‰ Workflow completed!')
        for workflow in final_status:
            print(f'šŸ“Š {workflow["code"]}: {workflow["status"]}')
            for step in workflow['steps']:
                if 'locations' in step and step['locations']:
                    print(f'šŸ”— View result: {step["locations"][0]}')

    except Exception as error:
        print(f'āŒ Error: {error}')

if __name__ == '__main__':
    quick_start_example()
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

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

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

    private async Task<T> RequestAsync<T>(string endpoint, HttpMethod method = null, object data = null)
    {
        method = method ?? HttpMethod.Get;
        var request = new HttpRequestMessage(method, $"{_baseUrl}{endpoint}");

        if (data != null)
        {
            var json = JsonConvert.SerializeObject(data);
            request.Content = new StringContent(json, Encoding.UTF8, "application/json");
        }

        var response = await _httpClient.SendAsync(request);

        if (!response.IsSuccessStatusCode)
        {
            var error = await response.Content.ReadAsStringAsync();
            throw new Exception($"API Error: {error}");
        }

        var responseContent = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseContent);
    }

    public async Task<dynamic[]> GetWorkflowsAsync()
    {
        return await RequestAsync<dynamic[]>("/api/workflows");
    }

    public async Task<dynamic> ExecuteWorkflowAsync(string[] workflows, string[] mediaUrls, 
        Dictionary<string, object> metadata, object webhooks = null)
    {
        var payload = new
        {
            workflows,
            mediaUrls,
            metadata
        };

        return await RequestAsync<dynamic>("/api/workflows/execute", HttpMethod.Post, payload);
    }

    public async Task<dynamic[]> GetWorkflowStatusAsync(string correlationId)
    {
        return await RequestAsync<dynamic[]>($"/api/{correlationId}/status");
    }

    public async Task<dynamic[]> WaitForCompletionAsync(string correlationId, 
        int maxAttempts = 30, int intervalMs = 5000)
    {
        for (int attempt = 1; attempt <= maxAttempts; attempt++)
        {
            var status = await GetWorkflowStatusAsync(correlationId);

            bool allComplete = true;
            foreach (var workflow in status)
            {
                if (workflow.status != "Finished" && workflow.status != "Failed")
                {
                    allComplete = false;
                    break;
                }
            }

            if (allComplete)
            {
                return status;
            }

            Console.WriteLine($"ā³ Attempt {attempt}: Still processing...");
            if (attempt < maxAttempts)
            {
                await Task.Delay(intervalMs);
            }
        }

        throw new Exception("Workflow did not complete within the timeout period");
    }
}

// Usage
public class Program
{
    public static async Task QuickStartExample()
    {
        var client = new StorytellerClient("YOUR_API_KEY_HERE");

        try
        {
            // 1. Get available workflows
            Console.WriteLine("šŸ” Getting available workflows...");
            var workflows = await client.GetWorkflowsAsync();
            Console.WriteLine("āœ… Available workflows: " + string.Join(", ", workflows.Select(w => w.code)));

            // 2. Execute a workflow
            Console.WriteLine("\nšŸš€ Executing workflow...");
            var result = await client.ExecuteWorkflowAsync(
                new[] { "add-media" },
                new[] { "https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4" },
                new Dictionary<string, object>
                {
                    ["https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4"] = 
                        new { Title = "Quick Start Example Video" }
                }
            );
            Console.WriteLine($"āœ… Workflow started! Correlation ID: {result.correlationId}");

            // 3. Wait for completion
            Console.WriteLine("\nā³ Waiting for workflow to complete...");
            var finalStatus = await client.WaitForCompletionAsync(result.correlationId);

            Console.WriteLine("\nšŸŽ‰ Workflow completed!");
            foreach (var workflow in finalStatus)
            {
                Console.WriteLine($"šŸ“Š {workflow.code}: {workflow.status}");
                foreach (var step in workflow.steps)
                {
                    if (step.locations != null && step.locations.Count > 0)
                    {
                        Console.WriteLine($"šŸ”— View result: {step.locations[0]}");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"āŒ Error: {ex.Message}");
        }
    }

    public static async Task Main(string[] args)
    {
        await QuickStartExample();
    }
}
#!/bin/bash
# Complete workflow execution script

# Configuration
API_KEY="YOUR_API_KEY_HERE"
BASE_URL="https://integrations.usestoryteller.com"
MEDIA_URL="https://cdn.coverr.co/videos/coverr-overhead-view-of-the-highway-8903/1080p.mp4"

echo "šŸ” Getting available workflows..."
WORKFLOWS=$(curl -s -X GET "$BASE_URL/api/workflows" \
  -H "x-storyteller-api-key: $API_KEY")

if [[ $? -eq 0 ]]; then
    echo "āœ… Available workflows: $WORKFLOWS"
else
    echo "āŒ Failed to get workflows"
    exit 1
fi

echo -e "\nšŸš€ Executing workflow..."
RESULT=$(curl -s -X POST "$BASE_URL/api/workflows/execute" \
  -H "x-storyteller-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"workflows\": [\"add-media\"],
    \"mediaUrls\": [\"$MEDIA_URL\"],
    \"metadata\": {
      \"$MEDIA_URL\": {
        \"Title\": \"Quick Start Example Video\"
      }
    }
  }")

if [[ $? -eq 0 ]]; then
    CORRELATION_ID=$(echo $RESULT | jq -r '.correlationId')
    echo "āœ… Workflow started! Correlation ID: $CORRELATION_ID"
else
    echo "āŒ Failed to execute workflow"
    exit 1
fi

echo -e "\nā³ Waiting for workflow to complete..."
MAX_ATTEMPTS=30
ATTEMPT=1

while [[ $ATTEMPT -le $MAX_ATTEMPTS ]]; do
    STATUS=$(curl -s -X GET "$BASE_URL/api/$CORRELATION_ID/status" \
      -H "x-storyteller-api-key: $API_KEY")

    # Check if all workflows are finished or failed
    COMPLETED=$(echo $STATUS | jq -r '.[].status' | grep -v -E "(Finished|Failed)" | wc -l)

    if [[ $COMPLETED -eq 0 ]]; then
        echo -e "\nšŸŽ‰ Workflow completed!"
        echo "šŸ“Š Final Status: $STATUS"

        # Extract and display any result locations
        LOCATIONS=$(echo $STATUS | jq -r '.[].steps[] | select(.locations != null) | .locations[0]')
        if [[ -n "$LOCATIONS" ]]; then
            echo "šŸ”— View results:"
            echo "$LOCATIONS"
        fi
        break
    fi

    echo "ā³ Attempt $ATTEMPT: Still processing..."
    sleep 5
    ((ATTEMPT++))
done

if [[ $ATTEMPT -gt $MAX_ATTEMPTS ]]; then
    echo "āŒ Workflow did not complete within the timeout period"
    exit 1
fi

Common Issues & Solutions#

āŒ "Invalid or missing API key"#

  • Solution: Verify your API key is correct and included in the x-storyteller-api-key header

āŒ "Media URL is not accessible"#

  • Solution: Ensure your URL returns HTTP 200 and is publicly accessible
  • Test: Run curl -I "your-media-url" to check accessibility

āŒ "Workflow not found"#

  • Solution: Use GET /api/workflows to see available workflow codes for your account

āŒ "Required metadata missing"#

  • Solution: Check workflow requirements using GET /api/workflows/{workflowId} and include all required metadata fields

Next Steps#

Now that you've successfully executed your first workflow, explore these advanced features:

šŸŽÆ Set Up Webhooks#

Instead of polling for status, receive real-time notifications:

const webhooks = {
  workflowProcessedWebhookUrl: 'https://your-app.com/webhooks/success',
  workflowFailedWebhookUrl: 'https://your-app.com/webhooks/failure'
};

šŸ“ Organize Content#

Use categories and collections to organize your content:

// Get available categories
const categories = await client.request('/api/categories');

// Add category to metadata
const metadata = {
  'https://example.com/video.mp4': {
    'Title': 'My Video',
    'categoryId': categories.categories[0].id
  }
};

šŸ” Validate Before Execution#

Use the validation endpoint to catch issues early:

const validation = await client.request('/api/workflows/validate', {
  method: 'POST',
  body: JSON.stringify({ workflows, mediaUrls, metadata })
});

if (!validation.isValid) {
  console.log('āŒ Validation errors:', validation.validationResults);
}

šŸ”„ Batch Processing#

Process multiple files efficiently:

const mediaUrls = [
  'https://example.com/video1.mp4',
  'https://example.com/video2.mp4',
  'https://example.com/video3.mp4'
];

const metadata = {};
mediaUrls.forEach((url, index) => {
  metadata[url] = { 'Title': `Video ${index + 1}` };
});

const result = await client.executeWorkflow(['add-media'], mediaUrls, metadata);

Support#

Need help? We're here for you:


Congratulations! šŸŽ‰ You've successfully integrated with the Storyteller API. Your media is now available in your Storyteller tenant and ready to be used in your applications.