BrandAttach docs

One verified token and done. Three ways to attach a brand: JavaScript, CSS, or REST.

Try the integration without writing any code. Open the test sites — three standalone HTML pages (customer portal, analytics dashboard, email preview), one per integration method. Brand them manually first, then paste a BrandAttach token to watch the same surface repaint.

Quickstart

  1. Create an account.
  2. Scan a domain to create a brand profile. Verify the domain to enable live tokens.
  3. Register your SaaS app in Apps and create an app token.
  4. In your app, accept a customer's BrandAttach brand token. Pass both tokens to BrandAttach.

For SaaS apps

Register your app once. Support every customer's brand token.

  1. Register your app in Apps.
  2. Add the origins where your app runs (e.g. https://*.vendor.com).
  3. Create a ba_app_live_… token. Store it as a server secret.
  4. Add a "BrandAttach token" field to your customer settings.
  5. When rendering, combine the customer's brand token with your app token. If the response includes approval_required, prompt the customer to ask their brand owner to approve your app.
<script
  src="https://brandattach.com/attach.js"
  data-brand="ba_live_<customer_brand_token>"
  data-app="ba_app_live_<your_app_token>">
</script>

<img data-ba-logo="navbar" alt="Company logo">
<img data-ba-logo="avatar" alt="">
<span data-ba-name></span>
<button class="ba-button">Continue</button>

Backend (server-rendered) example:

GET https://brandattach.com/api/brand/{{ customer.brandAttachToken }}
Authorization: Bearer {{ appToken }}

For brand owners

  • Verify your domain in the dashboard so you can issue live tokens.
  • Choose an access policy: Allow registered apps (recommended for most brands), Require approval, or Private.
  • Review which apps are using your brand and revoke any of them at any time.
  • Use /report to flag misuse.

JavaScript snippet

One line. Attaches CSS variables, replaces logos and brand names, exposes window.BrandAttach.

<script
  src="https://brandattach.com/attach.js"
  data-brand="ba_live_<customer_brand_token>"
  data-app="ba_app_live_<your_app_token>">
</script>

<img data-ba-logo="navbar" alt="Company logo">
<img data-ba-logo="avatar" alt="">
<span data-ba-name></span>
<button class="ba-button">Continue</button>

The script never stretches images: it sets object-fit: contain; max-width: 100%; height: auto;.

CSS-only integration

For server-rendered apps, link the theme stylesheet and use the CSS variables / utility classes.

<link rel="stylesheet" href="https://brandattach.com/api/brand/ba_live_xxx/theme.css">

<button class="ba-button">Continue</button>
<p style="color: var(--ba-color-primary)">Brand colour</p>

Available variables:

  • --ba-brand-name
  • --ba-color-primary, --ba-color-accent, --ba-color-background, --ba-color-surface, --ba-color-text, --ba-color-muted, --ba-color-border
  • --ba-color-success, --ba-color-warning, --ba-color-error, --ba-color-info
  • --ba-font-primary
  • --ba-radius-sm, --ba-radius-md, --ba-radius-lg
  • --ba-logo-primary, --ba-logo-login, --ba-logo-avatar, --ba-logo-footer

Helper classes:

  • .ba-button, .ba-text-primary, .ba-bg-primary
  • .ba-logo-navbar, .ba-logo-footer, .ba-logo-login, .ba-logo-avatar

REST API

GET https://brandattach.com/api/brand/ba_live_xxx
Authorization: Bearer ba_app_live_yyy

200 OK
{
  "brand": { "id": "brand_...", "name": "Acme", "domain": "acme.com", "version": 7 },
  "logos": { "primary": { "url": "...", "type": "horizontal", "source": "owner" }, ... },
  "logoPlacements": {
    "navbar": { "url": "...", "width": 160, "height": 40, "fit": "contain" },
    "avatar": { "url": "...", "width": 96,  "height": 96, "fit": "contain" }
  },
  "colors": {
    "primary": "#0057FF", "accent": "#FFB000", "background": "#FFFFFF",
    "surface": "#F8FAFC", "text": "#101828", "muted": "#667085",
    "border": "#EAECF0", "success": "#12B76A", "warning": "#F79009",
    "error": "#F04438"
  },
  "fonts": { "primary": "Inter", "fallback": "system-ui, ...", "source": "owner" },
  "radius": { "sm": "4px", "md": "8px", "lg": "16px" },
  "rules": { "doNotStretchLogo": true, "minimumLogoWidth": 120, ... },
  "trust": { "verified": true, "verifiedDomain": "acme.com" },
  "permissions": { "appRegistered": true, "approvalStatus": "approved", ... },
  "cache": { "ttl": 300, "staleWhileRevalidate": 600, "version": "brand_..-v7" }
}

All endpoints accept the token as a path parameter and return CORS-friendly responses.

MethodPathWhat it does
GET/api/brand/<token>Complete brand JSON, with fallbacks, version, and cache headers
GET/api/brand/<token>/theme.cssCSS variables + helpers
GET/api/brand/<token>/logo?slot=…Placement-aware logo (302 to processed asset, or inline initials SVG fallback)
GET/attach.jsDrop-in JS snippet

Logo placements

BrandAttach classifies every logo by shape and serves the right one for each UI slot — aspect ratio is always preserved.

SlotIdeal shapeUsed in
navbarhorizontalSite header, app top bar
footerhorizontalPage footer
loginhorizontal or squareLogin / signup cards
avataricon / squareProfile chip, badge
faviconiconBrowser tab
emailhorizontalTransactional email header
app_iconicon / squarePWA, mobile shortcut
cardhorizontal or squareMarketing cards, hero
<img data-ba-logo="navbar" alt="Company logo">
<img data-ba-logo="avatar" alt="Company icon">
GET https://brandattach.com/api/brand/ba_live_xxx/logo?slot=navbar&theme=auto
→ 302 Redirect to the processed image URL (or original SVG / inline initials fallback)

If no logo is available, BrandAttach returns a generated initials SVG so your UI never breaks.

BIMI Connect

BrandAttach uses BIMI as an additional trust signal and as a source of square logo assets. We do not issue VMC/CMC certificates, and BIMI alone does not guarantee inbox display in any mail client.

  • Check your DNS for a BIMI record at default._bimi.<domain>.
  • Validate DMARC and SPF readiness.
  • Import your BIMI logo as a logo_bimi BrandAttach asset for avatar/email slots (your primary logo is never overwritten without explicit confirmation).
  • Use a VMC (Verified Mark Certificate) or CMC (Common Mark Certificate) for verified-mark display in Gmail/Yahoo — BrandAttach reports the certificate type but does not issue them.

React example

// React example
import { useEffect, useState } from "react";

export function useBrand(brandToken: string, appToken: string) {
  const [brand, setBrand] = useState<any>(null);
  useEffect(() => {
    fetch(`https://brandattach.com/api/brand/${brandToken}`, {
      headers: { Authorization: `Bearer ${appToken}` }
    }).then(r => r.json()).then(setBrand);
  }, [brandToken, appToken]);
  return brand;
}

Security model

BrandAttach's rule is one verified token and done. The safety system sits behind the scenes so the integration stays a single script tag, but every guardrail below applies to the public APIs.

Test vs live tokens

  • ba_test_… — for development. Works against any brand, no origin restriction. Generated fallbacks are allowed.
  • ba_live_… — for approved apps. Requires a verified brand, complete required fields (or approved fallbacks), and at least one allowed origin.
  • ba_app_test_… / ba_app_live_… — identify the SaaS app making the request. Live production requests should pass both tokens.

Brand access policy

Brand owners choose how registered apps can use their brand:

  • Allow registered apps — registered apps can use the brand by default; brand owners can revoke at any time.
  • Require approval — every app must be explicitly approved.
  • Private — only invited apps can use the brand.

High-risk use cases (login, payment, email, advertising) require approval by default even on permissive policies.

Domain verification

Verify ownership of the brand domain in the dashboard. Three methods:

  • DNS TXT: _brandattach.<domain> with value ba-verify=<code>.
  • Meta tag: <meta name="brandattach-verify" content="ba-verify-…">.
  • Well-known file: text at /.well-known/brandattach-verify.

Origin restrictions

Live tokens carry an allowlist of origins. Registered apps also carry their own list of origins. The public APIs read the request's Origin header (falling back to Referer) and reject anything outside both lists. Wildcard patterns are supported:

https://app.acme.com    // exact origin
https://*.acme.com      // any subdomain of acme.com
*.acme.com              // protocol-agnostic wildcard
acme.com                // bare host, https implied

Requests with neither Origin nor Referer are treated as server-to-server and allowed — the threat model is browser-based misuse.

Caching + TTL

  • Brand JSON: max-age=300, stale-while-revalidate=600, with ETag.
  • theme.css: max-age=900, stale-while-revalidate=2400, with ETag.
  • Logo assets (processed/versioned): max-age=31536000, immutable.
  • Logo fallbacks (generated SVG): max-age=86400, stale-while-revalidate=604800.
  • Permission decisions: TTL 120s for allows; denied responses use private, no-store.
  • Brand owners can recompile or force-rescan from the dashboard.

Revocation

  • Revoked brand or app tokens return 403 (and the brand owner sees the denied attempt).
  • Revoked approvals are honored within the permission TTL (default 120 s).

Usage logging

Every public API hit is logged with status (allowed / denied), endpoint, origin, app, and the resolved approval. Brand owners see this in their dashboard.

Misuse reports

Anyone can report a token being used to impersonate a brand at /report.

Domain scanning

  • Scans are SSRF-safe: private IPs, loopback, link-local, and non-HTTP(S) URLs are blocked.
  • Requests are bounded by timeout (≤9s), response size (≤2MB), and a small redirect cap.
  • If a scan fails or detection is poor, fill in the brand manually — every field is editable.
BrandAttach — Attach any brand to your site with one token