BrandparserInvite Only

5. Let Users Edit Their Brand

Build a brand editing UI in your app using the Brandparser section editing API

Let your users tweak their parsed brand data from inside your app. Change a primary color, update a font, revert to the original - all through the API.

What you'll build

A React component that displays a customer's brand colors and lets them edit them inline, with changes saved via the Brandparser API. The same pattern works for typography, tone, logos, and any other section.

The API

Brandparser provides three endpoints for editing brand sections:

MethodEndpointWhat it does
PATCH/v1/api/brands/:brandId/sections/:sectionMerge changes into existing data (JSON Merge Patch)
PUT/v1/api/brands/:brandId/sections/:sectionReplace the entire section
DELETE/v1/api/brands/:brandId/sections/:section/overridesRevert to the original parsed data

Sections: colors, typography, tone, mission, audiences, things, logos, images, art-direction, northstart-statement

Every edit is tracked in an audit log available via GET /v1/api/brands/:brandId/edit-history.

Step 1: Fetch the current brand

const API_KEY = process.env.BRANDPARSER_API_KEY!;
const BASE_URL = "https://api.brandparser.com";

async function getBrand(brandId: string) {
  const res = await fetch(`${BASE_URL}/v1/api/brands/${brandId}`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  const { data } = await res.json();
  return data;
}

Step 2: Patch a section

Use PATCH to merge changes into an existing section. Only the fields you send are updated. Set a field to null to remove it.

async function patchBrandSection(
  brandId: string,
  section: string,
  changes: Record<string, unknown>
) {
  const res = await fetch(
    `${BASE_URL}/v1/api/brands/${brandId}/sections/${section}`,
    {
      method: "PATCH",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/merge-patch+json",
      },
      body: JSON.stringify(changes),
    }
  );

  if (!res.ok) {
    const error = await res.json();
    throw new Error(error.message);
  }

  const { data } = await res.json();
  return data; // Updated section data
}

Example: update the primary color in the palette:

await patchBrandSection(brandId, "colors", {
  primary_palette: [
    {
      name: "Brand Blue",
      hex: "#2563EB",
      rgba: "rgba(37, 99, 235, 1)",
      hsl: "hsl(217, 91%, 60%)",
      usage: "Primary brand color, CTAs, links",
      category: "primary",
    },
  ],
});

Step 3: Revert to parsed data

If a user wants to undo their changes and go back to what Brandparser originally parsed:

async function revertSection(brandId: string, section: string) {
  const res = await fetch(
    `${BASE_URL}/v1/api/brands/${brandId}/sections/${section}/overrides`,
    {
      method: "DELETE",
      headers: { Authorization: `Bearer ${API_KEY}` },
    }
  );

  if (!res.ok) {
    const error = await res.json();
    throw new Error(error.message);
  }

  const { data } = await res.json();
  return data; // Original parsed data
}

Step 4: Build a color editor component

Here's a React component that lets users edit brand colors inline:

"use client";

import { useState } from "react";

type PaletteColor = {
  name: string;
  hex: string;
  usage: string;
  category?: string;
};

export function BrandColorEditor({
  brandId,
  initialColors,
}: {
  brandId: string;
  initialColors: PaletteColor[];
}) {
  const [colors, setColors] = useState(initialColors);
  const [saving, setSaving] = useState(false);
  const [saved, setSaved] = useState(false);

  function updateColor(index: number, hex: string) {
    const updated = [...colors];
    updated[index] = { ...updated[index], hex };
    setColors(updated);
    setSaved(false);
  }

  async function save() {
    setSaving(true);
    try {
      await fetch(`/api/brands/${brandId}/colors`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ primary_palette: colors }),
      });
      setSaved(true);
    } finally {
      setSaving(false);
    }
  }

  async function revert() {
    setSaving(true);
    try {
      const res = await fetch(`/api/brands/${brandId}/colors/revert`, {
        method: "POST",
      });
      const { colors: original } = await res.json();
      setColors(original);
      setSaved(true);
    } finally {
      setSaving(false);
    }
  }

  return (
    <div>
      <h3>Brand Colors</h3>
      <div style={{ display: "flex", gap: "1rem", flexWrap: "wrap" }}>
        {colors.map((color, i) => (
          <div key={i} style={{ textAlign: "center" }}>
            <label>
              <input
                type="color"
                value={color.hex}
                onChange={(e) => updateColor(i, e.target.value)}
                style={{
                  width: "64px",
                  height: "64px",
                  border: "2px solid #e2e8f0",
                  borderRadius: "8px",
                  cursor: "pointer",
                  padding: 0,
                }}
              />
              <div style={{ fontSize: "13px", marginTop: "4px" }}>
                {color.name}
              </div>
              <div style={{ fontSize: "11px", color: "#666" }}>
                {color.hex}
              </div>
            </label>
          </div>
        ))}
      </div>

      <div style={{ marginTop: "1rem", display: "flex", gap: "0.5rem" }}>
        <button onClick={save} disabled={saving}>
          {saving ? "Saving..." : saved ? "Saved" : "Save Changes"}
        </button>
        <button onClick={revert} disabled={saving} style={{ color: "#666" }}>
          Revert to Original
        </button>
      </div>
    </div>
  );
}

Step 5: Add API routes to proxy the requests

Your Next.js API routes proxy between your frontend and the Brandparser API, keeping your API key server-side:

// app/api/brands/[brandId]/colors/route.ts
import { NextRequest, NextResponse } from "next/server";

const API_KEY = process.env.BRANDPARSER_API_KEY!;
const BASE_URL = "https://api.brandparser.com";

export async function PATCH(
  request: NextRequest,
  { params }: { params: { brandId: string } }
) {
  const body = await request.json();

  const res = await fetch(
    `${BASE_URL}/v1/api/brands/${params.brandId}/sections/colors`,
    {
      method: "PATCH",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/merge-patch+json",
      },
      body: JSON.stringify(body),
    }
  );

  const data = await res.json();
  return NextResponse.json(data, { status: res.status });
}
// app/api/brands/[brandId]/colors/revert/route.ts
import { NextRequest, NextResponse } from "next/server";

const API_KEY = process.env.BRANDPARSER_API_KEY!;
const BASE_URL = "https://api.brandparser.com";

export async function POST(
  _request: NextRequest,
  { params }: { params: { brandId: string } }
) {
  const res = await fetch(
    `${BASE_URL}/v1/api/brands/${params.brandId}/sections/colors/overrides`,
    {
      method: "DELETE",
      headers: { Authorization: `Bearer ${API_KEY}` },
    }
  );

  const data = await res.json();
  return NextResponse.json(
    { colors: data.data?.primary_palette ?? [] },
    { status: res.status }
  );
}

Step 6: Wire it together

// app/customer/[customerId]/brand/page.tsx
import { BrandColorEditor } from "@/components/brand-color-editor";

const API_KEY = process.env.BRANDPARSER_API_KEY!;
const BASE_URL = "https://api.brandparser.com";

export default async function BrandSettingsPage({
  params,
}: {
  params: { customerId: string };
}) {
  // Look up brandId from your database
  const brandId = await getBrandIdForCustomer(params.customerId);

  const res = await fetch(`${BASE_URL}/v1/api/brands/${brandId}`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
    next: { revalidate: 60 },
  });
  const { data: brand } = await res.json();

  return (
    <div style={{ padding: "2rem" }}>
      <h1>Brand Settings</h1>
      <p>
        We parsed your brand from {brand.source_url}. Adjust anything
        that doesn't look right.
      </p>

      <BrandColorEditor
        brandId={brandId}
        initialColors={brand.analysis.colors.primary_palette}
      />
    </div>
  );
}

Extending to other sections

The same PATCH pattern works for any section. A few examples:

Update voice summary:

await patchBrandSection(brandId, "tone", {
  voice_summary: "Professional yet approachable",
});

Update the heading font:

await patchBrandSection(brandId, "typography", {
  font_families: [
    {
      name: "Poppins",
      usage: "Headings",
      availability: {
        availability: "google_fonts",
        google_fonts_css_url:
          "https://fonts.googleapis.com/css2?family=Poppins:wght@600;700&display=swap",
      },
    },
  ],
});

Check what's been changed:

const res = await fetch(
  `${BASE_URL}/v1/api/brands/${brandId}/edit-history?section=colors`,
  { headers: { Authorization: `Bearer ${API_KEY}` } }
);
const { data: edits } = await res.json();
// Each edit includes before/after data, who made the change, and when

Tips

  • PATCH vs PUT: Use PATCH for partial updates (merge into existing data). Use PUT only when you want to replace an entire section from scratch.
  • Null removes fields: In a PATCH request, setting a key to null removes it from the section data. This follows the JSON Merge Patch spec (RFC 7396).
  • Edit history is automatic: Every PATCH, PUT, and DELETE is recorded with before/after data. You can show users a changelog of their edits.
  • Revert is non-destructive: The DELETE overrides endpoint removes manual edits and returns the original parsed data. The edit history still shows what was changed.
  • Proxy your API key: Never expose your Brandparser API key to the browser. Use server-side API routes (as shown in Step 5) to proxy requests.

Next steps

On this page