Viewrium Docs

Authentication

Secret and publishable API keys, the Bearer header, the error envelope, and rate limits.

The Viewrium API lives at https://api.viewrium.com. Every request authenticates with an API key passed in the Authorization header:

Authorization: Bearer sk_live_...

Both SDKs and the embed set this header for you.

Key types

There are two kinds of key. They are not interchangeable - the API enforces what each type may do.

Secret keys (sk_)

  • Run server-side only. A secret key can read and write everything its scopes allow: create products, list them, review them, publish them, manage webhooks, read usage, and more.
  • Never expose an sk_ key in a browser, mobile app, or public repository. Treat it like a password.
  • Create and revoke secret keys in the dashboard under Settings -> Advanced -> API keys. A secret key's material is shown exactly once, on create.

Publishable keys (pk_)

  • Safe to ship in a browser. A publishable key is origin-gated: it only works from the domains listed on its project's allowed_domains, and it is limited to two operations - fetching a single published product (GET /v1/products/{id}) and sending analytics beacons (POST /v1/beacon).
  • What it receives from GET /v1/products/{id} is not the full product but an explicit allow-list (id, title, model_url, thumbnail_url, model_status), and only for a published product with a ready model. Anything else is a 404.
  • This is exactly what the <viewrium-model> embed needs, and nothing more.
Secret (sk_)Publishable (pk_)
Where it runsServerBrowser
AccessFull (scoped)Read one published product + send beacons
Origin restrictionNoneGated to the project's allowed_domains
PowersThe SDKsThe embed

Scopes

Keys can carry scopes that narrow what a secret key may do:

ScopeCovers
products:readListing, reading and exporting products
products:writeCreating, updating, reviewing, publishing and deleting products
usage:readGET /v1/usage
webhooks:manageWebhook endpoint CRUD and the deliveries list
beacon:writePOST /v1/beacon (publishable keys hold this implicitly)

A key with no scopes has full access for its type. A non-empty list is an explicit restriction: anything not named is denied. Publishable keys are capped structurally at products:read + beacon:write whatever their stored scopes say. Assign scopes when you create a key in the dashboard.

The error envelope

Every non-2xx response uses one shape:

{
  "error": {
    "code": "conflict",
    "message": "custom_id already exists in this organization",
    "details": null
  }
}
  • code - a stable, machine-readable string.
  • message - a human-readable explanation.
  • details - optional structured context (for example, per-field validation errors, or the reason an order was refused).

The SDKs parse this into typed exceptions so you can branch on the failure:

Python
from viewrium import Viewrium, ConflictError, AuthenticationError

client = Viewrium("sk_live_...")
try:
    client.products.retrieve("does-not-exist")
except AuthenticationError:
    ...  # bad or missing key (401)
except ConflictError as err:
    print(err.code, err.message, err.details)
TypeScript
import { Viewrium, ConflictError, AuthenticationError } from "@viewrium/sdk";

const client = new Viewrium({ apiKey: "sk_live_..." });
try {
  await client.products.retrieve("does-not-exist");
} catch (err) {
  if (err instanceof AuthenticationError) {
    // bad or missing key (401)
  } else if (err instanceof ConflictError) {
    console.log(err.code, err.message, err.details);
  }
}

Status to error mapping

HTTPPythonJavaScriptMeaning
400BadRequestErrorBadRequestErrorMalformed request (a resume that changes the quote, say)
401AuthenticationErrorAuthenticationErrorMissing or invalid key
402PaymentRequiredErrorPaymentRequiredErrorThe organization's plan does not allow this - no plan, a used free trial, a failed payment, or past the plan's active-product cap (details.reason says which)
403PermissionDeniedErrorPermissionErrorValid key, not allowed (scope, origin, or role)
404NotFoundErrorNotFoundErrorNo such resource - or one outside your reach
409ConflictErrorConflictErrorConflict (duplicate custom_id, a replayed resume_token, an order that cannot be placed)
422UnprocessableEntityErrorUnprocessableEntityErrorValidation failed
429RateLimitErrorRateLimitErrorRate limited - or, with code: "quota_exceeded", not enough credits for a generation (details.shortfall_credits)
5xxServerErrorServerErrorThe API failed to handle the request
-APIConnectionErrorConnectionErrorThe request never reached the API

Every SDK error extends ViewriumError and exposes status, code, message, and details.

Not found, not forbidden. A resource outside your organization's reach answers 404, not 403: it does not exist for you. 403 means the resource is visible and your role or scope is not enough.

Rate limits

When you exceed a rate limit the API returns 429 (RateLimitError). Back off and retry with exponential delay.

On this page