Skip to main content

Overview

The Haystack API provides programmatic access to all content management, media processing, search, and analytics features. This reference documents all available endpoints with request/response schemas.

Base URLs

Haystack provides two APIs with different base URLs:

Private API (Authenticated Endpoints)

All authenticated endpoints (content management, media, analytics) use:
https://api.thehaystack.ai/v2/bevly

Search API (Public, No Authentication)

The search endpoint uses a church-specific base URL:
https://{your-church-shortname}.thehaystack.ai/api
To find your Search API base URL:
  1. Log in to your Haystack Dashboard
  2. Navigate to DeveloperAPI
  3. Copy your Search API Base URL from the Search API section

Authentication

Most endpoints require Bearer token authentication. Include your API token in the Authorization header:
Authorization: Bearer YOUR_API_TOKEN
Important: The Search endpoint does NOT require authentication and should be called without an API token from public-facing applications. See the Authentication guide for details.
See the Authentication guide for complete details on obtaining and managing API tokens.

API Structure

The Haystack API is organized into the following resource groups:

Content Management

Items

Create, read, update, and delete content items

Collections

Organize items into top-level collections

Speakers

Manage people who deliver content

Topics

Tag content with topical categories

Media

Media Assets

Upload and manage video/audio files

Discovery

Search

Semantic search across content

Analytics

Logging

Track user interactions and events

Request Format

HTTP Methods

  • GET - Retrieve resources
  • POST - Create new resources
  • PATCH - Update existing resources
  • DELETE - Remove resources

Content Type

All POST and PATCH requests should include:
Content-Type: application/json
Media file uploads use a two-step process: first create a media asset via the API to receive a signed upload URL, then PUT the file directly to that URL. See the Media Management guide for details.

Query Parameters

Use query parameters for filtering, sorting, and pagination:
GET /items?collectionId=1&status=published&_orderBy=date,DESC&_limit=20&_offset=0
Common parameters:
  • _limit - Maximum number of records to return (default: 50, max: 100)
  • _offset - Number of records to skip for pagination (default: 0)
  • _orderBy - Sort order as array of strings in format ‘fieldName,direction’ (e.g., ‘title,ASC’)
  • _expand - Related entities to include in the response (can specify multiple)

Response Format

Success Response

Successful requests return a JSON object with the requested data:
{
  "item": {
    "id": 42,
    "title": "Example Item",
    "date": "2025-01-15"
  }
}
List endpoints include pagination metadata:
{
  "items": [...],
  "limit": 50,
  "offset": 0,
  "page": 0,
  "totalRecords": 150,
  "totalPages": 3,
  "hasMore": true
}

Error Response

Errors return a standardized format:
{
  "result": "error",
  "error": {
    "type": "ValidationError",
    "message": "Title is required"
  }
}

HTTP Status Codes

CodeMeaning
200Success
201Created
400Bad Request - Invalid parameters
401Unauthorized - Invalid or missing token
403Forbidden - Insufficient permissions
404Not Found - Resource doesn’t exist
422Unprocessable Entity - Validation failed
500Internal Server Error

Pagination

List endpoints support pagination using _limit and _offset:
// Get 50 items starting from offset 50 (second page)
const response = await fetch(
  `${API_URL}/items?_limit=50&_offset=50`,
  {
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`
    }
  }
);

const data = await response.json();
console.log(`Page ${data.page + 1} of ${data.totalPages}`);
console.log(`Showing ${data.items.length} of ${data.totalRecords} total items`);
console.log(`Has more: ${data.hasMore}`);
Use _expand to include related data in a single request:
# Get item with related data
GET /items/42?_expand=collection&_expand=series&_expand=speakers&_expand=mediaAssets
Available expansions:
  • Items: collection, series, speakers, mediaAssets, scriptures
  • Series: collection, items
  • Collections: items, series

Filtering

Most list endpoints support filtering by fields:
# Filter items by multiple criteria
GET /items?collectionId=1&seriesId=5&status=published

# Using filter operators
GET /items?date.gt=2025-01-01&status.ne=draft
Available filter operators:
  • No operator - Exact match (e.g., collectionId=1)
  • .eq - Equal to (e.g., status.eq=published)
  • .ne - Not equal to (e.g., status.ne=draft)
  • .in - In array (e.g., id.in=1,2,3)
  • .lt - Less than (e.g., date.lt=2025-01-01)
  • .le - Less than or equal
  • .gt - Greater than
  • .ge - Greater than or equal
  • .bt - Between two values (e.g., date.bt=2025-01-01,2025-12-31)
  • .nl - Is null (e.g., seriesId.nl=true)
  • .like - Pattern matching (e.g., title.like=%prayer%)

Sorting

Sort results using _orderBy:
# Get newest items first
GET /items?_orderBy=date,DESC

# Get topics by display order
GET /topics?_orderBy=displayOrder,ASC

# Multiple sort fields
GET /items?_orderBy=date,DESC&_orderBy=title,ASC