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:
| Method | Endpoint | What it does |
|---|---|---|
PATCH | /v1/api/brands/:brandId/sections/:section | Merge changes into existing data (JSON Merge Patch) |
PUT | /v1/api/brands/:brandId/sections/:section | Replace the entire section |
DELETE | /v1/api/brands/:brandId/sections/:section/overrides | Revert 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 whenTips
- PATCH vs PUT: Use
PATCHfor partial updates (merge into existing data). UsePUTonly when you want to replace an entire section from scratch. - Null removes fields: In a PATCH request, setting a key to
nullremoves 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
- Onboard a customer - parse a brand before editing it
- Branded UI - display brand data with live theming
- Brand Sections API - full PATCH endpoint reference
- Edit History API - audit log endpoint reference