NOVAI for developers

Also as Markdown for agents: /developers/design.md

NOVAI Design Review API

This guide is for AI agents and the developers who build them.

You send the API a website. It answers with three things:

Use it to make a site look current, crafted and fresh, and to check that your changes worked.

It works like the NOVAI SEO API (Site Validation). The loop is review, fix, then review again with previous_review_id to get a diff.

1. Quick facts

Base URLhttps://novaipulse.com
For agents with a keyPOST /api/v1/design/reviews, GET /api/v1/design/reviews/{review_id}
For agents without a keyMCP tool review_design on https://novaipulse.com/mcp (a summary, rate limited)
For NOVAI systemsPOST /api/pulse/design/reviews, GET /api/pulse/design/reviews/{id}, GET /api/pulse/design/capabilities (PULSE_API_KEY)
Auth (agents)Authorization: Bearer npk_... or X-API-Key: npk_.... Get a key at https://novaipulse.com/developers#pricing
CostOne review counts as one scan of your plan: Developer 500 a month, Pro 5,000 a month
Limits60 design reviews an hour per key. The MCP tool: 12 per 10 minutes per IP, and a site reviewed in the last day is answered from that review
Speed1 to 5 seconds. The answer is synchronous; there is nothing to poll
FormatJSON in, JSON out

2. What it measures

NOVAI reads the start page and its stylesheets:

It reads them like a browser, through the same SSRF guard as the crawler: public addresses only, the DNS answer pinned and every redirect checked. It never runs the site's scripts and never logs in.

There are five areas. Each is scored 0 to 100. The overall score is the weighted mean.

AreaWeightWhat it checks
typography25 %A chosen typeface or a 2005 system font; novelty fonts (Comic Sans, Papyrus, Courier); more than three families; body size under 16px; leading under 1.4; a timid, fixed headline scale
color20 %Body text contrast under 4.5:1 (WCAG 2.2 1.4.3); pure black on pure white; more than 40 one-off colours; several fully saturated colours competing; no colour tokens
layout25 %Not set up for phones; no responsive rules; table layouts and legacy tags; fixed pixel widths; float columns; more than 8 menu links; no call-to-action button; no headline
modernity15 %Bootstrap 2/3, jQuery 1.x or a 2010 to 2015 WordPress theme; many inline styles; only JPEG/PNG; images without width and height; no favicon; no share image
polish15 %Keyboard focus hidden; no interaction motion at all; motion that ignores reduced-motion; no dark mode; no shape or depth system

The overall score maps to a grade:

The review also returns two hints:

3. Review a site

POST /api/v1/design/reviews
Authorization: Bearer npk_...
Content-Type: application/json

{
  "url": "https://andersson-ror.se",   // required, a public http(s) address or a bare domain
  "industry": "rörmokare",              // optional; otherwise read from the page's title and description
  "brand_color": "#1b4f63",             // optional; otherwise the site's own brand colour, when it has one
  "family": "",                         // optional: force a style family (see §5)
  "previous_review_id": "",             // optional: the review you fixed, for a diff
  "direction": true                     // false: skip the direction (smaller answer)
}

The answer looks like this (shortened):

{
  "version": "novai-design-review-1",
  "review_id": "dr_7c1e0a...",
  "url": "https://andersson-ror.se/",
  "status": "done",
  "score": {"overall": 42, "grade": "outdated",
            "areas": {"typography": 52, "color": 55, "layout": 0, "modernity": 52, "polish": 67}},
  "era": {"label": "before 2014", "why": "table layout, legacy HTML tags or plugins"},
  "style_now": "none",
  "confidence": "high", "confidence_note": "",
  "summary": "Design score 42/100 (outdated); the code looks before 2014. Fix first: ...",
  "measured": { "fonts": ["arial"], "body_size_px": 12, "text_contrast": 2.85, "grid": 0, "...": "..." },
  "fixes": [ { "priority": 1, "code": "LAYOUT_LEGACY_MARKUP", "...": "..." } ],
  "direction": { "family": "crisp", "palette": { "...": "..." }, "fonts": { "...": "..." },
                 "tokens_css": ":root{--bg:...}", "components_css": "...", "...": "..." },
  "seconds": 2.1
}

confidence is high, medium or low, with confidence_note. It says how much of the CSS could be read. With little CSS (styles on another host, or drawn by JavaScript after loading), typography and colour are partly unmeasured and their scores say less.

status is done or unreadable. An unreadable review returns error (for example start_page_unreadable) and http_status.

Read a stored review again with GET /api/v1/design/reviews/{review_id}. You can only read your own key's reviews.

4. A fix

Every fix has the same shape. Do them in priority order.

FieldWhat it is
priority1 is first. The order is severity, then expected points
codeThe check, for example TYPE_DEFAULT_FONT, COLOR_LOW_CONTRAST, LAYOUT_FIXED_WIDTH
areatypography, color, layout, modernity or polish
severitycritical, high, medium or low
title, why, howWhat is wrong, why it matters to visitors, and the steps to fix it
css_exampleWhere it helps: CSS that fixes it, written in the NOVAI design tokens (--bg, --fg, --brand, --font-display, --r-m ...)
evidenceWhat NOVAI measured, for example {"body_size_px": 12}
expected_pointsOur estimate of how much the overall score rises when the fix is done
effortlow, medium or high
sourceThe standard behind it, when there is one (WCAG 2.2, Core Web Vitals)
verifyHow the next review decides that it is fixed

All the codes are listed in GET /api/pulse/design/capabilities (checks). That list is built from the code, so it is always current.

5. The direction (what makes it "cool, modern and fresh")

The direction is not text from a language model. It is the recipe the NOVAI design library composes for this business, the same composer that designs every site NOVAI builds (app/design/compose.py).

What the library holds:

How the recipe is built:

  1. Industry. The business's industry selects the moods, the style-family weights and the colour schemes.
  2. Brand colour. The site's own brand colour, or the one you send, becomes the palette. When there is none, a curated library palette is used.
  3. The rest. The composer then picks one variant for every element.
FieldWhat it is
family, family_descriptionThe recommended style family: soft, crisp, editorial, bold, brutal, organic, luxe, tech, playful or glass
alternativesTwo other families that fit the industry
moodsWhat the design should express, for example trustworthy, friendly, modern
palettebrand, accent, scheme (light, paper, mist, dark ...), from_site (true when built from the site's own colour) and library_palette (the curated palette's id otherwise)
fontsThe pairing: display and body family names, pairing id
tokens_cssOne :root{...} block ready to paste: every colour token (every text and background pair meets AA, body text 7:1), the type scale (--fs-h1 fluid with clamp), radius, shadow, spacing and motion tokens
components_cssThe library's CSS for buttons, cards, headings, focus rings, links and badges in this recipe. It styles the class names in §6 and uses only the tokens
componentsEach picked library variant (hero, header, services, call to action, footer, button, card ...) with its id, name and notes, so an agent can describe or rebuild the layout
section_order, rhythmThe home page's section order, and how section backgrounds alternate
templatesThree of the 500 art-directed templates for this industry (hero layout, navigation, motion, tone, sections, palette) for inspiration
playbookThe industry's design objective, the proof that persuades, the page order and what to avoid
motionThree low-cost motion ideas with how to replace them under reduced motion
novai_recipeThe recipe itself (family, slots, palette, fingerprint), in the format NOVAI's site builder stores

To try another look, send family (one of the ten). The recipe is then composed within that family.

6. Applying the direction to a site

tokens_css and components_css style a small set of shared class names. Map your markup to them, or rename the selectors to yours.

ClassElement
a.btn.btn-primary, a.btn.btn-secondary, a.btn.btn-ghostButtons, at least 44px high, with an optional <span class="btn-ico">→</span>
article.card with span.ico, h3, pCards
.h-display (h1), .h-title (section h2), .h-subHeadings
.eyebrow, .chip, .badgeSmall labels
.prose a, .tlinkText links
:focus-visibleThe focus ring

A safe order for an agent:

  1. Paste tokens_css and load the two fonts as self-hosted WOFF2, with font-display: swap.
  2. Point body at var(--font-body), var(--fg) and var(--bg), and headings at var(--font-display).
  3. Fix the critical and high fixes, using their css_example.
  4. Add components_css and use the classes for buttons, cards and headings.
  5. Review again with previous_review_id.

7. Verify: the diff

Send the next review with "previous_review_id": "<the review you fixed>". The answer then has:

"diff": {"fixed": ["TYPE_DEFAULT_FONT", "COLOR_LOW_CONTRAST"], "still_open": ["LAYOUT_FLOATS"],
         "new": [], "score_before": 42, "score_now": 71, "change": 29}

The previous review must be your own and of the same site. Otherwise no diff is returned.

8. For agents without a key: the MCP tool

The tool is review_design on the public MCP server (https://novaipulse.com/mcp, JSON-RPC 2.0, Streamable HTTP). It is also listed in /.well-known/mcp.json and /.well-known/agent.json.

{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
 "params": {"name": "review_design", "arguments": {"url": "andersson-ror.se", "industry": "rörmokare"}}}

It answers with:

It does not include the tokens, the component CSS or the full fix list. Those come from the keyed API.

9. Errors

StatuserrorMeaning
400bad_urlNot a public http(s) address
401missing_key, bad_keyNo key, or not a valid one
402plan_inactiveThe payment failed or the plan was cancelled
404review_not_foundNo design review with that id for this key
429quota_exceeded, rate_limitedMonthly quota used, or more than 60 reviews this hour

10. Examples

curl

curl -s -X POST https://novaipulse.com/api/v1/design/reviews \
  -H "Authorization: Bearer $NOVAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://yoursite.com", "industry": "dental clinic"}' | jq '.score, .fixes[0], .direction.fonts'

JavaScript

const r = await fetch("https://novaipulse.com/api/v1/design/reviews", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.NOVAI_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({ url: "https://yoursite.com" }),
});
const review = await r.json();
// review.direction.tokens_css -> paste into your global stylesheet
// review.fixes                -> work through them in priority order

An agent's loop

1. review = POST /api/v1/design/reviews {url}
2. apply review.direction.tokens_css and the fonts
3. for fix in review.fixes where severity in (critical, high): apply fix.css_example / fix.how
4. deploy
5. again = POST /api/v1/design/reviews {url, previous_review_id: review.review_id}
6. stop when again.score.grade == "fresh", or again.diff.still_open is empty

11. How NOVAI itself uses it

The design is optimised the same way as SEO, through the same API.

12. Limits and honest notes