Authentication

One Bearer key, a set of scopes, and a brand you are allowed to touch. Everything else follows from those three.

The key

Mint keys in Settings → API & Webhooks → API Keys. Send yours as a Bearer token on every request:

curl "https://vm.viraldashboard.io/api/public/v1/brands" \
  -H "Authorization: Bearer vd_live_…"

Keys look like vd_live_… or vd_test_… followed by 40 characters.

The secret is displayed exactly once, at creation. We store only a hash, so we cannot show it to you again and we cannot recover it for you. Lost a key? Delete it and mint a new one — deletion takes effect immediately.

Treat a key like a password: server-side only. It carries the permissions of the user who created it, so a key embedded in a browser or a mobile app hands those permissions to anyone who opens the developer tools.

Live keys and test keys

PrefixReadsWrites
vd_live_YesYes
vd_test_YesNo403 test_mode_write_forbidden

A test key is for wiring up and exploring against your real data without any risk of publishing. It is not a sandbox with fake data: it reads the same brands and posts a live key would, it simply refuses to change anything.

Scopes

Each key carries an explicit list of scopes, and every endpoint names the one it needs. Calling an endpoint whose scope you did not grant returns 403 insufficient_scope — even if the key's user could do it in the dashboard.

Scopes come in read/write pairs per resource:

ScopeCovers
brands:read / brands:writeListing, reading and creating brands
accounts:read / accounts:writeSocial accounts: list, connect, refresh, disconnect
posts:read / posts:writePosts: list, read, create, update, delete, submit
media:read / media:writeMedia library: list, upload, import from URL
analytics:readPost and account analytics
linkinbio:read / linkinbio:writeLink-in-Bio pages
content:read / content:writeContent plans and scheduling plan items
feeds:read / feeds:writeRSS feeds and articles
webhooks:read / webhooks:writeWebhook endpoints, secrets and deliveries
ai:writeAI captions, hashtags and images (consumes credits)
reports:read / reports:writeReports, exports and schedules

Grant the narrowest set that does the job. A key that only publishes needs posts:write and accounts:read — not webhooks:write, and certainly not ai:write, which can spend credits.

Brands are authorized, not filtered

Almost every path is nested under a brand: /brands/{brand}/posts. The key's user must be a member of that brand. If they are not, you get 403 brand_forbidden.

Note the deliberate choice: a brand you cannot reach is a 403, not an empty 200. Silently returning nothing would let a bug in your code look like "that brand has no posts". The API would rather tell you that you were denied.

What each failure means

StatusCodeMeaning
401unauthorizedNo key, malformed key, or a key that has been deleted.
403insufficient_scopeValid key, but it lacks the scope this endpoint needs.
403brand_forbiddenValid key, but its user is not a member of that brand.
403test_mode_write_forbiddenA vd_test_ key attempted a write.

A 401 means fix the key; a 403 means the key is fine, the permission is not. Never retry either — nothing about them is transient. See Errors for the full catalog.