Scope: the public Brandfolder REST API ("V4", plus the separate v1 webhooks service) and the adjacent Insights Data Connector (BigQuery). Since the Smartsheet acquisition the documentation lives on the Smartsheet developer portal under /api/brandfolder, but the API itself still runs on brandfolder.com S3 S19. The Smartsheet API (api.smartsheet.com/2.0) and the Smartsheet MCP server are different products and do not expose Brandfolder assets S20.
At a glance
https://brandfolder.com/api/v4 for everything except webhooks; https://brandfolder.com/api/v1 for /webhooks* S1 S3. Binary uploads go to signed Google Cloud Storage URLs returned by /upload_requests S1. Single global host — no tenant subdomains and no regional API hosts are documented S3. CDN delivery: cdn.bfldr.com / cdn.brandfolder.io S51 S52.md) and the portal publishes llms.txt S16. Archived copy on DAM Wiki: API spec downloads. No Postman collectionAuthentication
- Each user finds a personal API key on the profile page, Integrations tab (
https://brandfolder.com/profile#integrations), and copies it with the icon next to the key. - Send it on every call:
Authorization: Bearer {BF_API_KEY}. The spec also declaresAccept: application/jsonandContent-Type: application/jsonas required header parameters on most operations, including GETs S1. - The same key authenticates the v1 webhooks service (
Content-Type: application/json+ Bearer); the key is checked against the subscribed Brandfolder S1.
Permission model — responses only include resources the key's user can access S3. Roles: Owner (organization level only), Admin, one optional custom role (Collaborator abilities plus selected admin capabilities), Collaborator, Guest; permissions flow downward from Organization → Brandfolder → Collection; Portals and Brandguides are separate permissible objects S28. The API enum for permission_level lists only guest, collaborator, admin, owner S1; how the custom role is represented is Unknown.
Behaviour of a missing or bad key — list endpoints answer 200 with an empty list rather than 401/403 (/organizations, /brandfolders), while /upload_requests answers 403 and the v1 webhooks service answers 403 S18. Integrators therefore need an explicit key check (non-empty /organizations, or GET/users/whoami) S9 Unverified (/users/whoami is documented only in a recipe, is absent from the spec, and returned 404 without a key S18).
Other identity products — Smartsheet's own API/MCP use Smartsheet tokens and OAuth; they are not a way to authenticate to Brandfolder S20. Content Automation (Outfit) has its own separate SAML app S69.
Core resources
Paths relative to https://brandfolder.com/api/v4 unless stated. Inventory from the spec S1; items marked † are used in official recipes/SDK but absent from the spec.
| Resource | Endpoints | Notes |
|---|---|---|
| Organizations | GET/organizations, GET/organizations/{id} | fields=asset_count (+ cname on fetch, added to docs 2025-11-24 S7); include=brandfolders,brandguides,collections,portals |
| Brandfolders (libraries) | GET/brandfolders, GET/PUT/brandfolders/{id}, POST/organizations/{id}/brandfolders | Attributes name, privacy, slug, tagline; list fields=asset_count,attachment_count,storage; fetch fields=asset_count,attachment_count,section_count, include=organization,collections,search_filters,sections; 422 on invalid/non-unique slug |
| Sections | GET/POST/brandfolders/{id}/sections, GET/sections/{id} | default_asset_type ∈ GenericFile, Color, Font, ExternalMedium, Person, Press, Text; no update/delete endpoint; GET/collections/{id}/sections † S12 |
| Collections (incl. Workspaces) | GET/collections, GET/POST/brandfolders/{id}/collections, GET/PUT/DELETE/collections/{id} | Attributes name, slug, tagline, public, stealth, is_workspace |
| Assets (list / search) | GET/brandfolders/{id}/assets, GET/collections/{id}/assets, GET/sections/{id}/assets, GET/labels/{id}/assets | page, per, search, fields=created_at,updated_at,cdn_url,availability, include=attachments,brandfolder,collections,custom_fields,section,tags (section list has no include in the spec) |
| Assets (single) | GET/PUT/DELETE/assets/{id} | Fetch additionally allows fields=availability_start,availability_end; PUT sets name, description, availability_start/end, approved, and adds attachments (never replaces) |
| Asset creation | POST/brandfolders/{id}/assets, POST/collections/{id}/assets | Bulk: data.attributes is an array of assets, each with attachments[{url, filename}]; section_key required S1 S14 |
| Attachments (files) | GET/PUT/DELETE/attachments/{id}; GET/assets/{id}/attachments † S13 | fields=metadata,thumbnail_url,view_thumbnail_retina,extension,version_count,tag_names,label_names; include=asset,section,brandfolder,collections,tags,labels; created only via asset create/update |
| Tags | GET/brandfolders/{id}/tags, GET/collections/{id}/tags, GET/POST/assets/{id}/tags, PUT/tags/{id}, DELETE/async/tags/assets/{asset_key} | One tag record per asset; async bulk delete body {"tags":[…],"locale":"en"} S1 S17 |
| Metadata schema (custom field keys) | GET/POST/brandfolders/{id}/custom_field_keys, PUT/DELETE/custom_field_keys/{id} | Per Brandfolder; fields=value, include=custom_field_values; deleting a key deletes its values |
| Metadata values | GET/assets/{id}/custom_field_values, POST/custom_field_keys/{key_id}/custom_field_values (one key → many assets), PUT/DELETE/custom_field_values/{id} | All values are strings |
| Labels (hierarchical taxonomy) | GET/POST/brandfolders/{id}/labels, GET/PUT/DELETE/labels/{id}, PUT/labels/{id}/move, GET/labels/{id}/assets | Feature not enabled on every account S1; no endpoint to assign an asset to a label |
| Users / permissions | `GET /organizations | brandfolders |
| Invitations | GET/`POST /organizations | brandfolders |
| Current user | GET/users/whoami † | S9 Unverified not in spec |
| Uploads | GET/upload_requests → PUT/resumable to GCS | See Uploads & binaries |
| Renditions / derivatives | cdn_url field (asset lists), attachment url, thumbnail_url, view_thumbnail_retina | On-the-fly transforms via Smart CDN URL parameters S51; no rendition endpoint in the API |
| Rights | availability_start / availability_end / computed availability, approved | No dedicated rights/licence object S1 S37 |
| Workflows / approvals | approved boolean only | Approval configuration, approvers, Workspace tasks, comments and annotations have no endpoints S1 |
| Webhooks (v1) | GET/POST/webhooks, GET/DELETE/webhooks/{id}, POST/webhooks/send | See Events & webhooks |
| Analytics (bulk) | Insights Data Connector (BigQuery) — not REST | See Data model S15 |
Not exposed in the documented API (checked against the spec S1): share links, portals and Brandguides content (only their invitations), user groups and SSO/SAML teams, comments/annotations, version history (only version_count), download/view analytics, asset history log, search-filter CRUD, asset automations, metadata conversions, duplicate-detection settings, moving an asset to another section, adding an existing asset to a collection or label, and bulk update/delete of assets.
Data model
- Hierarchy: Organization → Brandfolder → Section → Asset → Attachment(s). Assets can also belong to many Collections in the same Brandfolder; an asset lives in exactly one Section S3 S1. Portals and Brandguides hang off the Organization S28.
- Asset = a container; the files are Attachments (files, but also colours, fonts, external media links) S1. An asset can hold up to 75 attachments S49. The JSON:API
typeof an asset follows the asset type (generic_filesin all examples) S5; the Insights connector lists typesColor,ExternalMedium,Font,GenericFile,Person,Press,TextS15. - Asset attributes: default
name,description,thumbnail_url(signed, expiring),approved; optional viafields=:created_at,updated_at,cdn_url,availability, and on single fetchavailability_start/availability_endS1. - Attachment attributes:
mimetype,extension,filename,size(bytes),width,height,url,position,thumbnail_url; optionalmetadata,version_count,tag_names,label_namesS1. No checksum/hash field is documented S1. - Identifiers: opaque string keys (e.g.
oqgiju-21olts-ce9egi); the docs use "key" and "id" interchangeably (asset_key) S1 S13. Organizations, Brandfolders and Collections also have URLslugs S1. - Custom fields: schema is per Brandfolder (
custom_field_keys:name,allowed_values[],restricted,required,prioritized(≤ 5),position) S1. Each value is a separate key/value record attached to exactly one asset; keys and values are always strings S1. Multi-value, dependent fields (one level, ≤ 12 children per parent value) and translations exist in the UI but are not modelled in the API schema S38 S39 S1. - Tags: per-asset keyword records with
name, read-onlyauto_generatedand nullablesource(e.g.google_vision) — distinguishes AI tags from user tags S1. - Labels: hierarchical (
path[]of label ids,depthstarting at 1,position); spec wording ("starting with this label's ID") and example ordering are ambiguous Unverified S1. - Availability:
availability_start= publish date (Draft banner before),availability_end= expiry (Expired banner after) S1 S37. The Insights connector documentsavailabilityvaluespublished,unpublished,expired,pending approvalS15; whether the REST field uses the same strings is Unknown. - Dates: ISO 8601 with milliseconds and
Z(e.g.2018-09-11T20:31:34.858Z) S5; webhookevent_timeisYYYY-MM-DD HH:MM:SS.ffffffS1. - Insights Data Connector (paid analytics path): a customer-specific GCP project with BigQuery dataset
brandfolder(bfdc-<org-slug>.brandfolder.<table>), refreshed nightly; tablesassets(incl.created_by,availability),attachments,brandfolders,cdn_aggregates,collections,custom_field_values,events,sessions,tags,users(incl. SSO/SAML-team and last-active columns),user_permissions,sharelink,share_link_itemsS15. It is the only documented machine-readable source for events, share links and user activity.
Search & querying
| Aspect | Behaviour |
|---|---|
| Where | search= on the four asset-list endpoints and on GET/brandfolders S1 S10 |
| Syntax | Same as the UI search bar (Lucene-like), must be URL-encoded S5 S10. Default operator between bare terms is OR; AND/&&, NOT, * (trailing wildcard), ~ (fuzzy), "phrase", grouping tags:(a "b c"), ranges [a TO b], >/< S48 |
| Field filters | name:, asset_key:, asset_type:, filename:, extension:, description:, section:, collection:, labels:, tags: / tags.strict:, custom_fields.<Key>:"<Value>", metadata.<key>:, created_at:, updated_at:, published_at:, expires_at:, approved:, expired:, comments:, view_only:, size:, width:/height:, creator:, people.strict: S48 S11 |
| Empty / non-empty | NOT custom_fields.Key:(*) (key missing), NOT label: *, NOT expires_at:* S48 |
| Relative dates | created_at:>now-10m, updated_at:>now-1d, expires_at:[now TO now+14d] S48 |
| Facets / aggregations | None in the API; counts only via meta.total_count of a filtered query S5 |
| Sorting | sort_by ∈ name, score, position, updated_at, created_at with order ∈ ASC, DESC (both required together) S5 |
| Page size | per default 100, max 3000 S1 S5; fields/include "can slow down" responses S5. Behaviour above 3000 is Unknown (anonymous probe with per=5000 returned an empty 200, which proves nothing) S18 |
| Deep pagination / delta | No cursor and no since parameter S1; deltas rely on updated_at search/sort or webhooks. Maximum reachable page is Not public |
| Organization-wide crawl | Officially: list Brandfolders (?include=organization), then sections per Brandfolder, then GET/sections/{id}/assets?include=attachments S1 |
| Errors | 404 often means a misspelled custom-field key/value in the query; an empty 200 can mean bad syntax/encoding S11 |
| Reserved characters | > < ( ) { } [ ] " * : reserved; ! ? + - / ~ ^ = have special meaning S48 |
Uploads & binaries
From a public URL (bulk) S1 S14: POST/brandfolders/{id}/assets (or /collections/{id}/assets) with data.attributes[] = assets, each with attachments[{url, filename}], and a top-level section_key. The source URL must stay reachable "for at least a couple of hours"; attachment metadata (mimetype, size) is filled asynchronously after processing S1.
From a private/local file (binary upload) S1
- GET
/upload_requests→upload_url,resumable_upload_url, storage service type andobject_url(the spec schema names itservice, the proseservice_typeUnverified). PUTthe bytes toupload_url, or use the GCS resumable protocol: POSTresumable_upload_urlwithx-goog-resumable: start, thenPUTchunks. Resumable is recommended for poor connections, local files > 200 MB or server-side files > 500 MB.- Create the asset with
attachments[].url = object_url.
- URLs are single-use for one asset;
upload_url/resumable_upload_urlexpire after 24 h; objects atobject_urlare purged after 7 days.
New versions: PUT/attachments/{id} with a new url, or PUT/assets/{id} with extra attachments (added, not replacing) S1. The version list itself is UI-only; the API exposes version_count S1 S47.
Downloads & renditions
- Attachment
urlandthumbnail_urlare signed, expiring URLs S1. cdn_url(Smart CDN) is available when the CDN is included in the plan S13. Smart CDN URLs accept parameters such aswidth,height,pad=true,position=n(nth attachment), extension swap (e.g..png→.jpg),auto=webp(v2) or.auto(v1) S51. CDN links are disabled for pending, expired, draft or view-only assets S51; the spec documents a 404 on asset lists and a 403 on GET/assets/{id}"for CDN URLs of assets that are pending approval or unpublished" S1. Links may stay cached up to 24 h after expiry/deletion S51.- Custom CDN links by filename (
/<CDN-KEY>/fn/<filename>) are activated by support S51. Customers can front the origincdn.bfldr.comwith their own CDN, but lose automatic invalidation (Brandfolder suggests webhooks for that) S52. - Video: HLS stream link in the asset's Embed tab; iframe player with
loop,muted,autoplayparameters S51 S57.
Events & webhooks
- Host:
https://brandfolder.com/api/v1(a separate service:Server: gunicornS18). - Subscribe: POST
/webhooks{"data":{"attributes":{"event_type","resource_type":"brandfolder","resource_key":<brandfolder_id>,"callback_url"}}}; list active (GET/webhooks), fetch, delete. One subscription per event type per Brandfolder (409 if it already exists). - Events:
asset.create,asset.update(name, description, section, approval status, expiration status, comments & annotations, tags, custom fields — add/update/delete),asset.delete. A new asset fires create and then update (when processing completes). Edits made at Collection level also fire. No events for users, permissions, labels, collections or schema. - Payload ("skinny"):
{"data":{"attributes":{"key","event_time","event_type","brandfolder_key","organization_key"},"webhook_id"}}— the receiver must re-fetch the asset. - Delivery: acknowledge immediately with 2xx; otherwise up to 15 retries with exponential backoff; persistently failing subscriptions become inactive; ordering not guaranteed (sort by
event_time); subscriptions are deactivated within 30 minutes if the key's user loses access;callback_urlmust be public HTTPS. - Test: POST
/webhooks/send→ 202 and a fake notification tocallback_url. - Signature / HMAC: Not public (none documented).
- UI side: webhooks appear in the integrations directory as a developer option S81.
Limits & quirks
| Topic | Detail | |
|---|---|---|
| Silent empty lists | Missing/invalid key → 200 + empty list on list endpoints, not 401 | S1 S18 |
| 403 / 404 semantics | 403 = missing/inappropriate key or deleted resource; for CDN URLs of pending/unpublished assets the spec lists 403 on GET/assets/{id} but 404 on asset lists | S6 S1 Unverified |
| Rate limit | Threshold Not public; counted per API key; no rate headers | S11 S18 |
| Required headers | Accept and Content-Type: application/json declared required even on GET | S1 |
| Bodies | attributes is an object for single updates but an array for bulk creates (assets, tags, custom field keys); custom-field-value creation uses data as an array of {attributes, relationships} | S1 S17 |
fields semantics | Adds non-default attributes (not a sparse fieldset) | S5 |
| Endpoints outside the spec | GET/users/whoami, GET/assets/{id}/attachments, GET/collections/{id}/sections are used in official recipes/SDK but not in the spec | S9 S12 S13 Unverified |
| Per-Brandfolder schema | Custom fields, labels and webhooks are all scoped to one Brandfolder; an organization has no shared schema | S1 |
| Tag/value rows | Renaming or deleting a tag or custom-field value affects only that asset's record | S1 |
| No checksum | No MD5/SHA field on attachments; native duplicate detection is UI-only, pixel-exact | S1 S45 |
| Section changes | No API to update/delete sections or to move an asset between sections | S1 |
| Deprecated org-wide listing | GET/organizations/{id}/assets and /attachments removed; Python SDK 2.0.0 warns org-level listing will break | S17 S21 |
| Stale doc links | SDK READMEs still point to developer(s).brandfolder.com, which now 301-redirects to the Smartsheet portal | S21 S19 |
Label path ordering | Spec description vs example ambiguous | S1 Unverified |
SDKs, tools & code samples
| Library / tool | Language | Maintainer | Status | |
|---|---|---|---|---|
brandfolder (brandfolder-sdk-python) | Python | Brandfolder / Smartsheet | 2.0.0 on PyPI (2024-12-20); thin client wrapper; repo last pushed 2024-12-20 | S21 S98 |
brandfolder/brandfolder-sdk-php | PHP | Brandfolder / Smartsheet | README: no longer under active development; last push 2024-01-10 (still listed on the portal) | S8 S22 |
@brandfolder-panel/sdk (Panel UI SDK) | JavaScript | Brandfolder (brandfolder-developers) | 0.9.33 (2024-12-17); iframes the Brandfolder panel for browse/upload in third-party apps | S8 S23 |
sketch-plugin-brandfolder | JavaScript | Brandfolder | Sketch plugin; last push 2026-04-14 | S24 |
orientier/brandfolder-sdk-php | PHP | Community fork | Last push 2025-07-28 | S25 |
contentstack/marketplace-brandfolder-app | TypeScript | Contentstack | Contentstack Marketplace app; last push 2026-09-10 | S25 |
damiankw/brandfolder.upload | Python | Community | Bulk uploader to Organization > Brandfolder > Section; last push 2026-01-13 | S25 |
moiso97barrantes-ship-it/Brandfolder-MCP | TypeScript | Community | Only public Brandfolder MCP repo found; no licence; single push 2026-04-23 | S25 |
| Postman collection | — | — | None published | S16 |
| Official Ruby / .NET / Java SDKs | — | — | None found | S98 |
Changelog of the API
| Date | Change | |
|---|---|---|
| 2025-11-24 | Docs fix: cname listed as a valid fields value on GET/organizations/{id} | S7 |
| 2025-07-10 | Docs fix: operation titles renamed ("Create custom field values for an asset", "Create tags for an asset") | S7 |
| 2025-02-03 | "API Endpoint Updates": endpoints unsuited to searching assets across Brandfolders deprecated (org-wide asset/attachment listing) | S83 S17 |
| 2024-12-20 | Python SDK 2.0.0 deprecates org-level asset/attachment listing | S21 |
| 2024-12-16 | Custom CDN links (by filename) | S83 |
| ≤ 2024-04 | DELETE/tags/{id} announced as "soon deprecated" in favour of DELETE/async/tags/assets/{asset_key} (exact date Unknown) | S17 |
| 2022-12-01 | SDK for Panel UI integration released | S83 |
| 2020-02-24 | Webhooks documentation improved (create/edit/delete asset events) | S83 |
| File | Format | Size | Download |
|---|---|---|---|
brandfolder_openapi_v4.yaml | OpenAPI | 207 KB | Download |