> ## Documentation Index
> Fetch the complete documentation index at: https://developer.thehaystack.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Managing Content

> Complete guide to creating and organizing content in Haystack

## Overview

This guide walks you through the complete workflow of managing content in Haystack, from creating your organizational structure to publishing items.

## Prerequisites

Before you begin, ensure you have:

* A Haystack account with API access
* Your API token (see [Authentication](/authentication))
* At least one collection created

## Step 1: Set Up Your Organization

### Create Collections

Collections are the top-level categories for your content. Create them first:

```javascript theme={null}
const collections = [
  { name: 'Sunday Sermons' },
  { name: 'Midweek Studies' },
  { name: 'Podcasts' }
];

for (const collection of collections) {
  const response = await fetch(`${API_URL}/collections/create`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(collection)
  });

  const { collection: created } = await response.json();
  console.log(`Created collection: ${created.name} (ID: ${created.id})`);
}
```

### Create Speakers

Add the people who deliver your content:

```javascript theme={null}
const speakers = [
  {
    name: 'Pastor John Smith',
    bio: 'Senior Pastor since 2015, passionate about biblical teaching and discipleship.'
  },
  {
    name: 'Sarah Johnson',
    bio: 'Youth Pastor and conference speaker focusing on student ministry.'
  }
];

for (const speaker of speakers) {
  const response = await fetch(`${API_URL}/speakers/create`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(speaker)
  });

  const { speaker: created } = await response.json();
  console.log(`Created speaker: ${created.name} (ID: ${created.id})`);
}
```

### Create Topics

Define the topics/tags you'll use to categorize content:

```javascript theme={null}
const topics = [
  { name: 'Prayer', displayOrder: 1 },
  { name: 'Faith', displayOrder: 2 },
  { name: 'Leadership', displayOrder: 3 },
  { name: 'Relationships', displayOrder: 4 },
  { name: 'Worship', displayOrder: 5 }
];

for (const topic of topics) {
  const response = await fetch(`${API_URL}/topics/create`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(topic)
  });

  const { topic: created } = await response.json();
  console.log(`Created topic: ${created.name} (ID: ${created.id})`);
}
```

## Step 2: Create a Series (Optional)

For multi-part content, create a series:

```javascript theme={null}
const seriesData = {
  title: 'Spiritual Disciplines',
  subTitle: 'A 4-Week Journey',
  description: 'Explore the foundational practices of prayer, fasting, worship, and scripture study that deepen your relationship with God.',
  shortDescription: 'Master the spiritual disciplines',
  collectionId: 1, // Sunday Sermons
  colorHex: '#4A90E2',
  itemSortDirection: 'ASC', // Show oldest first
  showItemOrderInSeries: true, // Show "Part 1", "Part 2", etc.
  published: true
};

const response = await fetch(`${API_URL}/series/create`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(seriesData)
});

const { series } = await response.json();
console.log(`Created series: ${series.title} (ID: ${series.id})`);
```

### Upload Series Artwork

Add visual branding to your series:

```javascript theme={null}
const formData = new FormData();
formData.append('file', seriesArtworkFile); // Your image file
formData.append('artworkType', 'square'); // or 'wide'

const response = await fetch(
  `${API_URL}/series/${series.id}/artwork/upload`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`
    },
    body: formData
  }
);
```

<Note>
  **Artwork requirements:**

  * Square: 1:1 aspect ratio, minimum 1000x1000px
  * Wide: 16:9 aspect ratio, minimum 1920x1080px
  * Max file size: 15MB
  * Formats: JPG, PNG
</Note>

## Step 3: Create an Item

Now create your first content item:

```javascript theme={null}
const itemData = {
  // Basic information
  title: 'The Power of Prayer',
  subTitle: 'Part 1 of Spiritual Disciplines',
  description: 'Discover how prayer transforms not just our circumstances, but our hearts. Learn practical ways to develop a consistent prayer life and experience deeper intimacy with God.',
  shortDescription: 'Transform your prayer life',
  date: '2025-01-15',

  // Required: Collection
  collectionId: 1,

  // Optional: Series
  seriesId: series.id,

  // Optional: Speakers (array of IDs)
  speakers: [
    { id: 1 } // Pastor John Smith
  ],

  // Optional: Topics (array of IDs)
  topics: [
    { id: 1 }, // Prayer
    { id: 2 }  // Faith
  ],

  // Publishing (initially draft)
  published: false
};

const response = await fetch(`${API_URL}/items/create`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(itemData)
});

const { item } = await response.json();
console.log(`Created item: ${item.title} (ID: ${item.id})`);
```

## Step 4: Add Item Artwork

Upload thumbnail images for the item:

```javascript theme={null}
// Upload square artwork (for grids)
const squareFormData = new FormData();
squareFormData.append('file', squareArtworkFile);
squareFormData.append('artworkType', 'square');

await fetch(`${API_URL}/items/${item.id}/artwork/upload`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`
  },
  body: squareFormData
});

// Upload wide artwork (for hero images)
const wideFormData = new FormData();
wideFormData.append('file', wideArtworkFile);
wideFormData.append('artworkType', 'wide');

await fetch(`${API_URL}/items/${item.id}/artwork/upload`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`
  },
  body: wideFormData
});

console.log('Artwork uploaded successfully');
```

## Step 5: Add Scriptures (Optional)

Link Bible passages to your item:

```javascript theme={null}
const scriptures = [
  {
    itemId: item.id,
    book: 'matthew',
    chapter: 6,
    verseStart: 9,
    verseEnd: 13
  },
  {
    itemId: item.id,
    book: '1_thessalonians',
    chapter: 5,
    verseStart: 17,
    verseEnd: 17
  }
];

for (const scripture of scriptures) {
  await fetch(`${API_URL}/scriptures/create`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(scripture)
  });
}

console.log('Scriptures added');
```

## Step 6: Publish the Item

Once everything is ready, publish the item:

```javascript theme={null}
const response = await fetch(`${API_URL}/items/${item.id}/publish`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`
  }
});

const { item: publishedItem } = await response.json();
console.log(`Published: ${publishedItem.title}`);
```

<Warning>
  Items must have at least one media asset attached before they can be published. See the [Working with Media](/guides/working-with-media) guide to learn how to upload media.
</Warning>

## Updating Content

### Update Item Metadata

Modify an existing item:

```javascript theme={null}
const updates = {
  title: 'The Transforming Power of Prayer', // Updated title
  description: 'Updated description...',
  topics: [
    { id: 1 }, // Prayer
    { id: 5 }  // Worship (added)
  ]
};

const response = await fetch(`${API_URL}/items/${item.id}`, {
  method: 'PATCH',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(updates)
});

const { item: updatedItem } = await response.json();
```

### Unpublish an Item

Take an item offline:

```javascript theme={null}
await fetch(`${API_URL}/items/${item.id}/unpublish`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_TOKEN}`
  }
});
```

## Bulk Operations

### Creating Multiple Items

When importing a library, create items in batches:

```javascript theme={null}
const items = [
  { title: 'Item 1', date: '2025-01-01', collectionId: 1 },
  { title: 'Item 2', date: '2025-01-08', collectionId: 1 },
  { title: 'Item 3', date: '2025-01-15', collectionId: 1 }
];

// Create items sequentially
for (const itemData of items) {
  const response = await fetch(`${API_URL}/items/create`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(itemData)
  });

  const { item } = await response.json();
  console.log(`Created: ${item.title} (ID: ${item.id})`);

  // Small delay to avoid rate limits
  await new Promise(resolve => setTimeout(resolve, 100));
}
```

<Tip>
  For large bulk imports (>100 items), consider batching your requests and implementing retry logic for failed imports.
</Tip>

## Querying Content

### List Items

Retrieve items with filtering and pagination:

```javascript theme={null}
// Get published items from a specific series
const response = await fetch(
  `${API_URL}/items?seriesId=5&published=true&sortBy=date&sortDirection=DESC&page=1&pageSize=20`,
  {
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`
    }
  }
);

const data = await response.json();
console.log(`Found ${data.total} items`);
console.log(`Showing page ${data.page} of ${Math.ceil(data.total / data.pageSize)}`);

data.items.forEach(item => {
  console.log(`- ${item.title} (${item.date})`);
});
```

### Get Item with Related Data

Fetch an item with all related data:

```javascript theme={null}
const response = await fetch(
  `${API_URL}/items/42?_expand[]=collection&_expand[]=series&_expand[]=speakers&_expand[]=mediaAssets`,
  {
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`
    }
  }
);

const { item } = await response.json();

// Now item includes nested objects
console.log(item.collection.name); // "Sunday Sermons"
console.log(item.series.title);    // "Spiritual Disciplines"
console.log(item.speakers[0].name); // "Pastor John Smith"
```

## Common Patterns

### Checking if Item Exists

```javascript theme={null}
async function itemExists(title, date, collectionId) {
  const response = await fetch(
    `${API_URL}/items?title=${encodeURIComponent(title)}&date=${date}&collectionId=${collectionId}`,
    {
      headers: {
        'Authorization': `Bearer ${API_TOKEN}`
      }
    }
  );

  const data = await response.json();
  return data.items.length > 0;
}
```

### Finding Items by Speaker

```javascript theme={null}
async function getItemsBySpeaker(speakerId) {
  const response = await fetch(
    `${API_URL}/items?speakerId=${speakerId}&_expand[]=speakers`,
    {
      headers: {
        'Authorization': `Bearer ${API_TOKEN}`
      }
    }
  );

  const data = await response.json();
  return data.items;
}
```

### Getting Latest Items

```javascript theme={null}
async function getLatestItems(limit = 10) {
  const response = await fetch(
    `${API_URL}/items?published=true&sortBy=date&sortDirection=DESC&pageSize=${limit}`,
    {
      headers: {
        'Authorization': `Bearer ${API_TOKEN}`
      }
    }
  );

  const data = await response.json();
  return data.items;
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Plan Your Collection Structure">
    Design your collections before importing content. Changing an item's collection later requires updating the item, which can affect analytics.
  </Accordion>

  <Accordion title="Use Consistent Naming">
    Maintain consistent naming conventions for speakers and topics to avoid duplicates.

    * ✅ "John Smith" (everywhere)
    * ❌ "John Smith", "Pastor John", "Rev. Smith" (creates duplicates)
  </Accordion>

  <Accordion title="Keep Items in Draft While Building">
    Create items as drafts, add all metadata and media, then publish when complete.
  </Accordion>

  <Accordion title="Use Series for Related Content">
    Group related items into series for better discovery and user engagement.
  </Accordion>

  <Accordion title="Add Comprehensive Metadata">
    Rich metadata (descriptions, topics, scriptures) improves search quality and user experience.
  </Accordion>
</AccordionGroup>

## Error Handling

Always handle errors gracefully:

```javascript theme={null}
async function createItemSafely(itemData) {
  try {
    const response = await fetch(`${API_URL}/items/create`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(itemData)
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error.message);
    }

    const { item } = await response.json();
    return item;
  } catch (error) {
    console.error('Failed to create item:', error.message);
    throw error;
  }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Working with Media" icon="video" href="/guides/working-with-media">
    Learn how to upload and manage media for your items
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Explore the full API documentation
  </Card>
</CardGroup>
