Skip to content

Assets API

The Assets API manages the files in the Runnit asset library: uploading, reading, downloading, updating with automatic version history, deleting, and organising with entity links, tags, and folders.

Assets always live inside a collection. Create or look up a collection with the Asset Collections API first, then upload into it.

Authentication: every endpoint requires an API key. See API Keys & Authentication.

Permissions: asset endpoints check ownership rather than role. Your key’s organisation must own the asset (or, for reads scoped to a client organisation, have an active client relationship with it). Requests outside that boundary return 403.

AI transformations of an asset (POST /api/v1/assets/:assetId/edit and POST /api/v1/assets/:assetId/convert-to-video) start generation jobs and are covered by Image Generation and Video Generation.

{
"id": "5595c819-...",
"fileName": "style-guide",
"fileType": "markdown",
"fileExtension": "md",
"mimeType": "text/markdown",
"isBinary": false,
"isRemoteUrl": false,
"storageProvider": "aws",
"storageKey": "assets/ddf75d91-.../d5df4edc-.../style-guide",
"fileSize": 32,
"description": "Tone of voice and formatting rules.",
"metadata": {},
"createdByAgentType": null,
"createdByUserId": "213ae491-...",
"createdByUserName": "Mark Tolson",
"updatedByAgentType": null,
"createdAt": "2026-07-08T02:31:50.117Z",
"updatedAt": "2026-07-08T02:31:58.598Z",
"version": 1,
"owningOrganizationId": "ddf75d91-...",
"visibilityScope": "organization",
"sourceType": "upload",
"isArchived": false,
"summaryExcerpt": "A short AI summary of the content.",
"searchStatus": "ready",
"folderPath": null,
"lastIndexedAt": "2026-07-08T12:31:58.597Z",
"folderIds": [],
"storageUrl": "https://...",
"url": "https://..."
}
  • fileType is derived from the extension: markdown, html, text, image, video, audio, json, csv, pdf, or other.
  • searchStatus is pending, processing, ready, or failed and tracks content indexing for search and insights.
  • content is included when the endpoint returns file content (create, update, and single-asset reads). For binary files it is base64.
  • storageUrl and url appear when the file is held in cloud storage.
  • entityLinks appears when you ask for links.

Upload a file. Send a JSON body; for binary files (images, PDFs, Office documents, media) put the base64-encoded bytes in content.

Give the file a home in one of two ways:

  • collectionId (preferred): the asset is stored in that collection.
  • entityLinks (legacy): Runnit finds or creates a suitable collection for the linked job, project, or organisation and stores the asset there.

Request body

NameTypeRequiredDescription
fileNamestringYesFile name.
fileExtensionstringYesExtension without meaning-changing tricks, e.g. md, png, pdf. Determines the file type and whether content is treated as binary.
contentstringYesFile content. Plain text for text formats, base64 for binary formats, or a URL when isRemoteUrl is true.
collectionIdUUIDOne of the two homesCollection to store the asset in.
entityLinksarrayOne of the two homesObjects with entityType (job, organization, or project) and entityId.
descriptionstringNoDescription stored on the asset.
metadataobjectNoFree-form metadata.
createdByAgentTypestringNoLabel recording which automation produced the file.
isRemoteUrlbooleanNotrue when content is a URL to an externally hosted file. Defaults to false.
owningOrganizationIdUUIDNoOwning organisation. Normally derived from the collection or links.
visibilityScopestringNoorganization, client, public, or private.
sourceTypestringNoWhere the file came from, e.g. upload.
externalSourcestringNoName of an external system of record.
externalIdstringNoID in that external system.
isArchivedbooleanNoCreate the asset already archived.
tagIdsarray of UUIDsNoTags to apply immediately.
folderIdsarray of UUIDsNoFolders to place the asset in.
primaryFolderIdUUIDNoWhich of folderIds is the primary folder.

Text uploads are rejected when the content looks like a placeholder (for example just placeholder, TBD, or [CONTENT HERE]). Send the real, final content.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/assets \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{
"fileName": "style-guide",
"fileExtension": "md",
"content": "# Style guide\n\nUse sentence case.",
"collectionId": "<collection-id>",
"description": "Tone of voice and formatting rules."
}'

Response

Returns 201 with the full asset, including content. The upload is also recorded as version 1 in the asset’s version history, and content indexing is queued (searchStatus starts as pending).

{ "success": true, "asset": { "id": "5595c819-...", "fileName": "style-guide", "version": 1 } }

Errors

  • 400 if a required field is missing or invalid, if neither collectionId nor entityLinks is given, or if the referenced collection does not exist.
  • 400 with error placeholder_content if text content looks like a placeholder.
  • 403 if you do not have access to the collection, a linked entity, or the owning organisation.

List assets in a collection, or the assets linked to an entity. Provide collectionId, or both entityType and entityId.

Query parameters

NameTypeRequiredDescription
collectionIdUUIDOne of the two filtersLists the collection’s assets.
entityTypestringWith entityIdjob, organization, or project.
entityIdUUIDWith entityTypeLists assets linked to this entity.

Example request

Terminal window
curl "https://api.runnit.io/api/v1/assets?collectionId=<collection-id>" \
-H 'Authorization: Bearer rnk_your_key'

Response

{ "success": true, "collectionId": "d5df4edc-...", "assets": [ { "id": "5595c819-...", "fileName": "style-guide" } ] }

The entity form omits collectionId. Asset objects in lists do not include content; fetch an asset by ID to read it.

Errors

  • 400 if you supply neither collectionId nor the entityType and entityId pair.
  • 403 if you do not have access to the collection or entity.
  • 404 if the collection does not exist.

Fetch one file by name for an entity. Text files come back as JSON with content; images and binary documents come back as the raw bytes with the file’s own content type.

Query parameters

NameTypeRequiredDescription
entityTypestringYesjob, organization, or project.
entityIdUUIDYesEntity the file is linked to.
fileNamestringYesExact file name.

Example request

Terminal window
curl "https://api.runnit.io/api/v1/assets/file?entityType=project&entityId=<project-id>&fileName=style-guide" \
-H 'Authorization: Bearer rnk_your_key'

Response

For text formats:

{ "success": true, "asset": { "id": "5595c819-...", "fileName": "style-guide", "content": "# Style guide..." } }

For images, PDFs, Office documents, and other binary formats, the response body is the file itself (Content-Disposition: inline). Remote-URL assets always return JSON with the URL in content.

You can also fetch a file with a clean path instead of query parameters: GET /api/v1/assets/:entityType/:entityId/:fileName. That form always serves the file content directly (redirecting for remote-URL assets), which suits embedding in src attributes.

Errors

  • 403 if you do not have access to the entity.
  • 404 if no asset with that name is linked to the entity.

List an entity’s assets grouped by file type.

Query parameters

NameTypeRequiredDescription
entityTypestringYesjob, organization, or project.
entityIdUUIDYesEntity to inspect.

Example request

Terminal window
curl "https://api.runnit.io/api/v1/assets/by-type?entityType=project&entityId=<project-id>" \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"success": true,
"assetsByType": {
"markdown": [ { "id": "5595c819-...", "fileName": "style-guide" } ],
"image": [ { "id": "8a41d2b0-...", "fileName": "hero.png" } ]
}
}

Errors

  • 403 if you do not have access to the entity.

Download all of an entity’s assets as a single zip archive.

Query parameters

NameTypeRequiredDescription
entityTypestringYesjob, organization, or project.
entityIdUUIDYesEntity whose assets to download.
creatorstringNoOnly include files with this creator label (an agent type, or manual for human uploads).

Example request

Terminal window
curl -L -o assets.zip \
"https://api.runnit.io/api/v1/assets/download?entityType=project&entityId=<project-id>" \
-H 'Authorization: Bearer rnk_your_key'

Response

A zip file (Content-Type: application/zip) containing every matching asset.

Errors

  • 403 if you do not have access to the entity.
  • 404 if the entity has no matching assets.

List folders, either for a collection or for an entity that owns a folder tree.

Query parameters

NameTypeRequiredDescription
collectionIdUUIDOne of the two filtersLists the collection’s folders.
ownerEntityTypestringWith ownerEntityIdorganization, project, or job.
ownerEntityIdUUIDWith ownerEntityTypeLists the entity’s folders.

Example request

Terminal window
curl "https://api.runnit.io/api/v1/assets/folders?ownerEntityType=organization&ownerEntityId=<org-id>" \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"success": true,
"folders": [
{ "id": "c1b2a3d4-...", "name": "Logos", "slug": "logos", "path": "/logos", "parentFolderId": null, "sortOrder": 0 }
]
}

Errors

  • 400 if you supply neither collectionId nor the owner pair.
  • 403 if you do not have access to the collection or entity.
  • 404 if the collection does not exist.

Create a folder, optionally nested under a parent folder.

Request body

NameTypeRequiredDescription
namestringYesFolder name.
collectionIdUUIDOne of the two homesCreates the folder in the collection’s organisation tree.
ownerEntityTypestringWith ownerEntityIdorganization, project, or job.
ownerEntityIdUUIDWith ownerEntityTypeEntity that owns the folder tree.
parentFolderIdUUIDNoParent folder for nesting.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/assets/folders \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "ownerEntityType": "project", "ownerEntityId": "<project-id>", "name": "Approved" }'

Response

Returns 201 with the new folder:

{ "success": true, "folder": { "id": "c1b2a3d4-...", "name": "Approved", "path": "/approved" } }

Errors

  • 400 if name is missing, or neither collectionId nor the owner pair is given.
  • 403 if you do not have access to the collection or entity.

Delete a folder. By default the assets inside are kept (they just lose the folder link); pass deleteAssets=true to delete them too.

Query parameters

NameTypeRequiredDescription
deleteAssetsbooleanNotrue also deletes every asset in the folder.

Example request

Terminal window
curl -X DELETE "https://api.runnit.io/api/v1/assets/folders/<folder-id>?deleteAssets=true" \
-H 'Authorization: Bearer rnk_your_key'

Response

{ "success": true }

Errors

  • 403 if you do not have access to the folder’s owner.
  • 404 if the folder does not exist.

Semantic search over asset content. Runnit indexes every asset’s content and description, so you can search with natural language rather than exact file names. Any organisation member can call it; results are limited to assets you can access.

Query parameters

NameTypeRequiredDescription
qstringYesNatural-language search text.
entityTypestringNoLimit results to assets linked to one entity type: job, organization, or project. Use with entityId.
entityIdstring (UUID)NoThe entity to limit results to. Use with entityType.
organizationIdstring (UUID)NoThe organisation to search in. Defaults to your key’s organisation.
tagIdsstringNoComma-separated tag ids; only assets with those tags are returned.
folderIdstring (UUID)NoOnly assets in this folder.
limitintegerNoMaximum results, 1 to 50. Default 20.

Example request

Terminal window
curl "https://api.runnit.io/api/v1/assets/search?q=brand%20tone%20of%20voice&limit=10" \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"success": true,
"results": [
{
"assetId": "5595c819-...",
"fileName": "style-guide",
"score": 0.87
}
]
}

Each result includes the matching asset’s identifying fields and a relevance score. Fetch the full content with GET /api/v1/assets/:assetId.

Errors

  • 400 if q is missing or empty.
  • 403 if you ask for an entity you cannot access.

Get one asset with its full content. The response format depends on the file:

  • Text formats return JSON with content.
  • Images, PDFs, Office documents, and other binary formats return the raw file bytes with the file’s content type.
  • Remote-URL assets return JSON with the URL in content.
  • format=json forces a JSON response for any file (binary content comes back base64-encoded).

Path parameters

NameTypeRequiredDescription
assetIdUUIDYesAsset ID.

Query parameters

NameTypeRequiredDescription
formatstringNojson forces a JSON response even for binary files.
includeLinksbooleanNotrue includes the asset’s entityLinks in JSON responses.

Example request

Terminal window
curl "https://api.runnit.io/api/v1/assets/<asset-id>?format=json&includeLinks=true" \
-H 'Authorization: Bearer rnk_your_key'

Response

{ "success": true, "asset": { "id": "5595c819-...", "fileName": "style-guide", "content": "# Style guide...", "version": 2, "entityLinks": [] } }

Errors

  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

Update an asset’s content or details. Changing content (or fileName) snapshots the previous state and creates a new version, so earlier versions stay available for restore.

Request body (all optional)

NameTypeRequiredDescription
contentstringNoNew file content. Text formats reject placeholder content.
fileNamestringNoNew file name.
descriptionstringNoNew description.
metadataobjectNoReplaces the stored metadata.
changelogstringNoMessage recorded on the new version.
updatedByAgentTypestringNoLabel recording which automation made the change.

Changing content, file name, description, or metadata also queues reindexing, so searchStatus returns to pending until the new content is indexed.

Example request

Terminal window
curl -X PATCH https://api.runnit.io/api/v1/assets/<asset-id> \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "content": "# Style guide v2\n\nUse sentence case everywhere.", "changelog": "Second pass" }'

Response

{ "success": true, "asset": { "id": "5595c819-...", "version": 2, "content": "# Style guide v2..." } }

Errors

  • 400 with error placeholder_content if the new text content looks like a placeholder.
  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

Delete an asset.

Example request

Terminal window
curl -X DELETE https://api.runnit.io/api/v1/assets/<asset-id> \
-H 'Authorization: Bearer rnk_your_key'

Response

{ "success": true, "message": "Asset deleted successfully" }

Errors

  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

Link an existing asset to another entity, so it also appears there.

Request body

NameTypeRequiredDescription
entityTypestringYesjob, organization, or project.
entityIdUUIDYesEntity to link.
requestIdstringNoFree-form reference stored on the link.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/assets/<asset-id>/links \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "entityType": "project", "entityId": "<project-id>" }'

Response

Returns 201 with the link:

{ "success": true, "link": { "assetId": "5595c819-...", "entityType": "project", "entityId": "1f0e9a52-..." } }

Errors

  • 403 if your organisation does not own the asset, or you do not have access to the target entity.
  • 404 if the asset does not exist.

Unlink an asset from an entity, identified by query parameters.

Query parameters

NameTypeRequiredDescription
entityTypestringYesjob, organization, or project.
entityIdUUIDYesEntity to unlink.

Example request

Terminal window
curl -X DELETE "https://api.runnit.io/api/v1/assets/<asset-id>/links?entityType=project&entityId=<project-id>" \
-H 'Authorization: Bearer rnk_your_key'

Response

{ "success": true, "message": "Asset unlinked from entity successfully" }

Errors

  • 403 if your organisation does not own the asset.
  • 404 if the asset or the link does not exist.

Apply existing tags to an asset. The first five supplied tags become the asset’s primary tags.

Request body

NameTypeRequiredDescription
tagIdsarray of UUIDsYesAt least one tag ID.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/assets/<asset-id>/tags \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "tagIds": ["<tag-id>"] }'

Response

{ "success": true, "tags": [ { "id": "7d31e0aa-...", "name": "Brand", "slug": "brand", "isSystem": false } ] }

Errors

  • 400 if tagIds is missing, empty, or contains a non-UUID value.
  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

DELETE /api/v1/assets/:assetId/tags/:tagId

Section titled “DELETE /api/v1/assets/:assetId/tags/:tagId”

Remove one tag from an asset.

Example request

Terminal window
curl -X DELETE https://api.runnit.io/api/v1/assets/<asset-id>/tags/<tag-id> \
-H 'Authorization: Bearer rnk_your_key'

Response

{ "success": true }

Errors

  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

Place an asset in one or more folders. Use replaceExisting to move it rather than add it.

Request body

NameTypeRequiredDescription
folderIdsarray of UUIDsYesAt least one folder ID.
primaryFolderIdUUIDNoWhich of folderIds is the primary folder.
replaceExistingbooleanNotrue removes the asset from all current folders first (a move).

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/assets/<asset-id>/folders \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "folderIds": ["<folder-id>"], "replaceExisting": true }'

Response

{ "success": true, "folders": [ { "id": "c1b2a3d4-...", "name": "Approved", "path": "/approved" } ] }

Errors

  • 400 if folderIds is missing or empty.
  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

Queue an asset for content reindexing. Use this if search results or the summary excerpt look stale.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/assets/<asset-id>/reindex \
-H 'Authorization: Bearer rnk_your_key'

Response

{ "success": true, "message": "Asset reindex scheduled" }

searchStatus moves to pending and returns to ready once indexing completes.

Errors

  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

Get an asset’s derived information: AI summary excerpt, indexing status, tags, and folders.

Example request

Terminal window
curl https://api.runnit.io/api/v1/assets/<asset-id>/insights \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"success": true,
"insights": {
"summaryExcerpt": "Tone of voice rules for all campaign copy.",
"searchStatus": "ready",
"lastIndexedAt": "2026-07-08T12:31:58.597Z",
"tags": [ { "id": "7d31e0aa-...", "name": "Brand" } ],
"folders": [ { "id": "c1b2a3d4-...", "name": "Approved" } ]
}
}

Errors

  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

List an asset’s version history, newest first (up to 50 versions). Each version includes its full content, so you can inspect a version before restoring it.

Example request

Terminal window
curl https://api.runnit.io/api/v1/assets/<asset-id>/versions \
-H 'Authorization: Bearer rnk_your_key'

Response

{
"success": true,
"versions": [
{
"id": "367be2ca-...",
"assetId": "5595c819-...",
"versionNumber": 2,
"fileName": "style-guide",
"fileSize": 47,
"content": "# Style guide v2...",
"changelog": "Second pass",
"createdByUserId": "213ae491-...",
"createdAt": "2026-07-08T02:32:22.652Z"
}
]
}

Errors

  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

POST /api/v1/assets/:assetId/versions/:versionId/restore

Section titled “POST /api/v1/assets/:assetId/versions/:versionId/restore”

Restore an asset to an earlier version. The asset’s content and name return to that snapshot.

Path parameters

NameTypeRequiredDescription
assetIdUUIDYesAsset ID.
versionIdUUIDYesVersion to restore, from the versions list.

Example request

Terminal window
curl -X POST https://api.runnit.io/api/v1/assets/<asset-id>/versions/<version-id>/restore \
-H 'Authorization: Bearer rnk_your_key'

Response

{ "success": true, "asset": { "id": "5595c819-...", "content": "# Style guide..." } }

Errors

  • 403 if your organisation does not own the asset.
  • 404 if the asset does not exist.

An end-to-end file lifecycle. Replace the placeholders with real IDs.

  1. Create a collection to hold the files:
Terminal window
curl -X POST https://api.runnit.io/api/v1/collections \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "ownerOrganizationId": "<org-id>", "name": "Winter Campaign Assets", "collectionType": "creative" }'
  1. Upload a file into it:
Terminal window
curl -X POST https://api.runnit.io/api/v1/assets \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{
"fileName": "campaign-brief",
"fileExtension": "md",
"content": "# Winter campaign brief\n\nGoals, audience, and deliverables.",
"collectionId": "<collection-id>"
}'
  1. Update the file, which creates version 2 automatically:
Terminal window
curl -X PATCH https://api.runnit.io/api/v1/assets/<asset-id> \
-H 'Authorization: Bearer rnk_your_key' \
-H 'Content-Type: application/json' \
-d '{ "content": "# Winter campaign brief v2\n\nUpdated goals.", "changelog": "Scope revision" }'
  1. Find files by listing the collection, or search asset content with natural language:
Terminal window
curl "https://api.runnit.io/api/v1/assets?collectionId=<collection-id>" \
-H 'Authorization: Bearer rnk_your_key'
curl "https://api.runnit.io/api/v1/assets/search?q=winter%20campaign%20goals" \
-H 'Authorization: Bearer rnk_your_key'

AI agents can run the same search through the MCP server with the same rnk_ key.

  1. Download the content. A single file:
Terminal window
curl "https://api.runnit.io/api/v1/assets/<asset-id>" \
-H 'Authorization: Bearer rnk_your_key' -o campaign-brief.md

Or everything linked to a project as a zip:

Terminal window
curl -L -o project-assets.zip \
"https://api.runnit.io/api/v1/assets/download?entityType=project&entityId=<project-id>" \
-H 'Authorization: Bearer rnk_your_key'

Organise files with the Asset Collections API, or report on the work they support with the Reports API.