Add Clip#
Creates Clips from source media. Use when you want Storyteller to ingest a media URL and create a Clip with optional publish status, categories, collections, thumbnail, and action links.
Clips are created as Published by default. Set the optional per-media PublishStatus metadata to Draft when a Clip needs editorial review, to Published with AvailableUntil when a live Clip should expire, or to Scheduled when it should go live in the future.
- Workflow ID:
add-clip
Basic Usage#
Minimal Example Request#
{
"workflows": [
"add-clip"
],
"mediaUrls": [
"https://cdn.example.com/videos/highlight-001.mp4"
],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight"
}
}
}
Because PublishStatus is omitted, this request creates a Published Clip.
Required Metadata#
| Key | Type | Required | Remarks |
|---|---|---|---|
Title |
string | Yes | Per-media. Clip title used internally. |
Minimal Example Request with Optional Metadata#
{
"workflows": [
"add-clip"
],
"mediaUrls": [
"https://cdn.example.com/videos/highlight-001.mp4"
],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"DisplayTitle": "Highlight",
"CreationTime": "2025-10-26T11:41:15+00:00",
"PublishStatus": "Draft"
}
}
}
Optional Basic Metadata#
| Key | Type | Required | Remarks |
|---|---|---|---|
DisplayTitle |
string | No | Per-media. Display title shown for the clip. If not provided, defaults to Title. |
CreationTime |
DateTime | No | Per-media. ISO 8601 creation time; defaults to current time if omitted. |
PublishStatus |
string | No | Per-media. Exact supported values are Draft, Published, and Scheduled. Defaults to Published when omitted or blank. |
AvailableFrom |
DateTimeOffset | No | Per-media. Strictly future inclusive start as an ISO 8601 date/time with an offset. Required, and only accepted, when PublishStatus is Scheduled. |
AvailableUntil |
DateTimeOffset or null | No | Per-media. Optional exclusive end as an ISO 8601 date/time with an offset. Accepted with Published or Scheduled; it must be future for Published and later than AvailableFrom for Scheduled. |
Creating a Draft Clip#
Use Draft when the Clip should be fully ingested and available to authorized CMS editors without appearing in published Clip feeds or viewer search. It becomes viewer-facing only after it is published.
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"PublishStatus": "Draft"
}
}
}
Values are case-sensitive. Values other than Draft, Published, and Scheduled are rejected.
Draft clears any existing AvailableFrom and AvailableUntil values. Availability fields supplied with Draft are rejected.
Publishing a Clip with an Expiry#
Use Published with an optional future AvailableUntil to make the Clip live immediately and expire it at a precise instant:
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"PublishStatus": "Published",
"AvailableUntil": "2026-08-12T00:00:00+01:00"
}
}
}
Published always clears AvailableFrom. At exactly AvailableUntil, the effective status is Archived and viewer-facing reads exclude the Clip. Storyteller then persists the archive and clears the completed availability window.
Scheduling a Clip#
Availability scheduling must be enabled for the tenant. Use Scheduled with an AvailableFrom value and, optionally, an AvailableUntil value:
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"PublishStatus": "Scheduled",
"AvailableFrom": "2026-08-04T18:00:00+01:00",
"AvailableUntil": "2026-08-05T00:00:00+01:00"
}
}
}
AvailableFrom must be later than the workflow request time. The window is half-open: the Clip is available at exactly AvailableFrom and unavailable at exactly AvailableUntil. AvailableUntil can be omitted for a schedule with no automatic expiry. Supplied offsets describe an instant and are normalized by Storyteller.
The scheduling feature is checked during tenant-aware workflow processing. If it is not enabled, metadata validation can succeed but the workflow step fails before the Clip is created or updated; use the workflow status endpoint to observe that failure.
Availability fields require an explicit compatible PublishStatus: AvailableFrom requires Scheduled, while AvailableUntil supports Scheduled or Published. When updating an existing Clip:
- omitting or leaving
PublishStatusblank preserves its editorial status and availability dates; Scheduledrequires and replaces a strictly futureAvailableFrom; omittingAvailableUntilpreserves the existing end date, while setting it tonullclears the end date;Publishedpublishes immediately and clearsAvailableFrom; an explicit futureAvailableUntilreplaces the end, explicitnullclears it, and omission preserves an existing future end;- a
Publishedupdate that would preserve an elapsed end is rejected, so a successful update cannot remain archived by an elapsed window; and Draftremoves the Clip from viewer-facing results and clears both availability dates.
cURL Example (Basic)#
curl -X POST "https://integrations.usestoryteller.com/api/workflows/execute" \
-H "x-storyteller-api-key: $ST_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<'JSON'
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"DisplayTitle": "Derby Day: 90s Highlight"
}
}
}
JSON
Advanced Usage#
Clips can be created with additional metadata to set up more properties to work with optional categories, collections, thumbnail, and action links.
External Identifiers#
Track clips using your own external identifier system:
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"ExternalId": "ext-clip-001"
}
}
}
Metadata:
| Key | Type | Remarks |
|---|---|---|
ExternalId |
string | External identifier for the clip asset that can be used for future reference. |
Action Links#
Associate primary and secondary action links with clips:
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"PrimaryActionLinkId": "11111111-1111-4111-8111-111111111111",
"SecondaryActionLinkIds": [
"22222222-2222-4222-8222-222222222222",
"33333333-3333-4333-8333-333333333333"
]
}
}
}
Metadata:
| Key | Type | Remarks |
|---|---|---|
PrimaryActionLinkId |
Guid | Primary action link associated with the clip. Must be a valid GUID of an existing action link. |
SecondaryActionLinkIds |
ArrayOf(Guid) | Additional action links. Each must be a valid GUID of an existing action link. |
Categories#
Using Existing Categories (by External ID)#
Both CategoryIds and Categories can be used to specify categories by their External IDs.
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"CategoryIds": ["match-highlights", "featured"]
}
}
}
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"Categories": [
{
"ExternalId": "match-highlights"
},
{
"ExternalId": "featured"
}
]
}
}
}
Creating New Categories#
To create a new category all of it's properties must be specified in Categories:
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"Categories": [
{
"ExternalId": "match-highlights",
"DisplayTitle": "Match Highlights",
"InternalTitle": "Match Highlights",
"AvailableForNavigation": true,
"ShowInClips": false,
"Type": "Other"
}
]
}
}
}
When creating a new category the following must be true:
ExternalIdis required and must be uniqueInternalTitlewill default toExternalIdif not provided, but must be unique in the systemDisplayTitleis optional (defaults toInternalTitleif not provided)ShowInClipsmust be set tofalse
Metadata:
| Key | Type | Remarks |
|---|---|---|
CategoryIds |
ArrayOf(string) | Category external IDs to associate with the clip. Categories must already exist. |
Categories |
ArrayOf(object) | Rich category objects for creating or referencing categories. |
Category Object Properties:
| Property | Type | Required | Remarks |
|---|---|---|---|
ExternalId |
string | Yes | Unique external identifier for the category. |
DisplayTitle |
string | No | Display title shown to users. |
InternalTitle |
string | No | Internal title for CMS use. Defaults to ExternalId if not provided. |
AvailableForNavigation |
bool | No | Whether category appears in navigation. Defaults to false. |
ShowInClips |
bool | No | Whether category is shown in clips list. Defaults to false. Currently only false is supported. |
Type |
string | No | Category type (e.g., "Other", "Player"). |
Collections#
Using Existing Collections (by Internal ID)#
Existing Collections can be referenced by their Internal ID specified in the Collections array:
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"Collections": [
{
"InternalId": "coll-100"
}
]
}
}
}
Creating New Collections#
Editorial collections can be created by specifying the type accordingly:
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"Collections": [
{
"InternalId": "coll-100",
"InternalTitle": "Season 2025 Highlights",
"DisplayTitle": "Season '25 Highlights",
"Type": "Editorial"
}
]
}
}
}
CategoryBased collections require the CategoryExternalId to be pointing to an existing Category by its External ID. In the following example the existing Category is also added to the Clip so it'll be included in the Collection from the Category.
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"Categories": [
{
"ExternalId": "premier-league"
}
],
"Collections": [
{
"InternalId": "coll-100",
"InternalTitle": "Season 2025 Highlights",
"DisplayTitle": "Season '25 Highlights",
"Type": "CategoryBased",
"CategoryExternalId": "premier-league"
}
]
}
}
}
Metadata:
| Key | Type | Remarks |
|---|---|---|
Collections |
ArrayOf(object) | Collections to associate the clip with. |
Collection Object Properties:
| Property | Type | Required | Remarks |
|---|---|---|---|
InternalId |
string | Yes* | Unique internal identifier. |
InternalTitle |
string | No | Internal title for CMS use. Defaults to InternalId. |
DisplayTitle |
string | No | Display title shown to users. Truncated to max length if needed. |
Type |
string | Yes | Collection type: "Editorial" or "CategoryBased". Editorial collections associate clips directly while Category Based collections include Clips when added to the category. |
CategoryExternalId |
string | No | Links collection to a category. Required when creating a category-based collection. |
Item Metadata#
Add arbitrary key-value metadata to clips:
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"ItemMetadata": {
"editor": "jdoe",
"series": "Derby Day",
"publisher_name": "example-publisher",
"production_date": "2025-10-01"
}
}
}
}
Metadata:
| Key | Type | Remarks |
|---|---|---|
ItemMetadata |
object | Arbitrary key-value pairs stored as clip metadata. Useful for tracking custom fields. |
Custom Thumbnails#
Static Thumbnail URL#
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"ThumbnailUrl": "https://cdn.example.com/images/highlight-001-thumb.jpg"
}
}
}
Template-Based Thumbnail#
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"ThumbnailTemplate": "thumb-template-01",
"TemplateMetadata": {
"style": "bold",
"watermark": "on"
}
}
}
}
Template with Custom Thumbnail#
{
"workflows": ["add-clip"],
"mediaUrls": ["https://cdn.example.com/videos/highlight-001.mp4"],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"ThumbnailUrl": "https://cdn.example.com/images/highlight-001-thumb.jpg",
"ThumbnailTemplate": "thumb-template-01",
"TemplateMetadata": {
"overlay": "team-logo"
}
}
}
}
Metadata:
| Key | Type | Remarks |
|---|---|---|
ThumbnailUrl |
string | Must be an absolute URL. Used as custom thumbnail or as base image for template. |
ThumbnailTemplate |
string | Thumbnail Template ID to apply. Can be used alone (uses auto-generated thumbnail) or with ThumbnailUrl. With a request-level template, ThumbnailUrl is required (see Studio Template Clips). |
TemplateMetadata |
object | Template-specific key-value pairs passed to the template processor. |
Thumbnail Processing Behavior:
ThumbnailUrlonly: Sets custom thumbnail directlyThumbnailTemplateonly: Applies template to auto-generated thumbnail from video- Both: Applies template to provided custom thumbnail URL
Studio Template Clips#
Set the request-level template to the external ID of a Studio template to render each Clip's video from that template instead of ingesting the media URL as the video. The template must be available to the Integrations API and have its template properties configured; each property's payload metadata key names the per-media metadata value that fills it.
{
"workflows": ["add-clip"],
"template": "example-clip-template",
"mediaUrls": ["https://cdn.example.com/images/summary-001.jpg"],
"metadata": {
"https://cdn.example.com/images/summary-001.jpg": {
"Title": "Morning Summary",
"ExternalId": "summary-001",
"CheckDuplicateExternalIds": true,
"ThumbnailUrl": "https://cdn.example.com/images/summary-001-thumb.jpg",
"ThumbnailTemplate": "example-thumbnail-template",
"headline": "Three things to know this morning",
"summary": "A short summary rendered by the template"
}
}
}
Template Clip Behavior:
- Each media URL creates one Clip and starts one Studio render of its video. The media URL is available to the template as the
mediaUrlpayload metadata key, for example to fill a background image or video. - Template values are matched to the template's properties by payload metadata key and must be sent as strings. Required properties without a value fail validation.
- A text property whose template element is a bulleted or numbered list takes its items as one newline-separated string, for example
"First point\nSecond point\nThird point". Each non-blank line becomes one list item, styled like the template's first item, and is shown as plain text: markup in a line is not interpreted. - Non-list text values are inserted as HTML, so callers must HTML-encode plain text, for example
Fans & Critics <React>. Newline-separated values for list elements are plain text and encoded per item, so don't encode them. - Image values use the image's real dimensions; the template's frame, scale and focal point decide the crop. If the image can't be read, the template's sample image dimensions are used.
- The video's duration is the media's duration for a video media URL, and the tenant's default Clip duration otherwise. A
durationvalue in seconds overrides this only when the template has adurationproperty and the request sends it. - All other Clip metadata applies as for other Clips:
ExternalIdandCheckDuplicateExternalIds, categories, collections, item metadata, action links, eyebrow andPublishStatuswith its availability fields. - The workflow finishes once the Clip is created and its render has started. The video renders and processes afterwards, and a
PublishedClip appears in Clip feeds only once its video is ready. ADraftClip stays a draft. ThumbnailUrlsets a custom thumbnail, which is used instead of the thumbnail generated from the rendered video.ThumbnailTemplateframes theThumbnailUrlimage with a thumbnail template, which reads it as itsthumbnailUrlpayload metadata key. With a request-leveltemplateit requires aThumbnailUrl, because the video is still rendering when the thumbnail is made; a request that sendsThumbnailTemplatewithoutThumbnailUrlis rejected with a validation error. The thumbnail template renders as soon as the Clip is created, and its output stays the Clip's thumbnail when the video finishes rendering. It also receives the request template's values, so a key that both templates name fills both.- A request with a
templatealways creates a new Clip. Updating a Clip is not supported with atemplate; a request that sends bothoperationIdandtemplateis rejected with a validation error.
Complete Advanced Example#
{
"workflows": ["add-clip"],
"mediaUrls": [
"https://cdn.example.com/videos/highlight-001.mp4"
],
"metadata": {
"https://cdn.example.com/videos/highlight-001.mp4": {
"Title": "Derby Day Highlight",
"DisplayTitle": "Derby Day: 90s Highlight",
"PublishStatus": "Draft",
"ExternalId": "ext-clip-001",
"ItemMetadata": {
"editor": "jdoe",
"series": "Derby Day",
"publisher_name": "example-publisher"
},
"PrimaryActionLinkId": "11111111-1111-4111-8111-111111111111",
"SecondaryActionLinkIds": [
"22222222-2222-4222-8222-222222222222",
"33333333-3333-4333-8333-333333333333"
],
"CategoryIds": ["match-highlights", "featured"],
"Categories": [
{
"ExternalId": "match-highlights",
"DisplayTitle": "Match Highlights",
"InternalTitle": "Match Highlights",
"AvailableForNavigation": true,
"ShowInClips": false,
"Type": "Other"
}
],
"Collections": [
{
"InternalId": "coll-100",
"InternalTitle": "Season 2025 Highlights",
"DisplayTitle": "Season '25 Highlights",
"Type": "Editorial",
"CategoryExternalId": "featured"
}
],
"ThumbnailUrl": "https://cdn.example.com/images/highlight-001-thumb.jpg",
"ThumbnailTemplate": "thumb-template-01",
"CreationTime": "2025-10-01T10:00:00Z",
"TemplateMetadata": {
"style": "bold",
"watermark": "on"
}
}
},
"workflowProcessedWebhookUrl": "https://partner.example.com/webhooks/processed",
"workflowFailedWebhookUrl": "https://partner.example.com/webhooks/failed"
}
Webhooks#
Both workflow completion and failure webhooks are optional but recommended for tracking workflow execution status:
| Key | Type | Required | Remarks |
|---|---|---|---|
workflowProcessedWebhookUrl |
string | No | URL called when workflow completes successfully. |
workflowFailedWebhookUrl |
string | No | URL called when workflow fails. |