Get started

Build for Baseet

Baseet is an Arabic-first commerce platform for MENA. This is where you learn to build on it — starting with storefront themes, and soon apps and the API.

What you can build today

  • Themes — complete storefront designs, expressed as data, not code, rendered by one trusted engine. Available
  • Apps & the public API — extend a store's functionality and data. Coming

A theme you publish can be installed by any Baseet merchant. You keep the majority of every sale (70/30 in your favor).

💡Early access. The developer program is invite-only while we harden it. If you have a developer key, you're in — head to the dashboard.

Quickstart

Scaffold, design, preview, and submit a theme — the whole loop — in about ten minutes.

1 · Get your key

The Baseet team issues you a scoped developer key (bth_live_…). It's the only credential you need, and it only authorizes submitting your own themes. Sign in at developers.baseet.dev and keep it somewhere safe — treat it like a password.

2 · Scaffold a theme

From a Baseet workspace checkout:

# creates the two DATA files + auto-registers the theme
npm run theme -- init my-theme --name-en "My Theme" --name-ar "ثيمي" --industry fashion

That's it — no React, no build config. You now have a manifest and a preset to edit.

3 · Make it yours

Edit the two generated files — the preset (design tokens + starter content) and the manifest (which sections, in what order). Point a demo store's theme_id at your id to see it live, then validate:

npm run theme -- validate my-theme

4 · Submit for review

export BASEET_THEME_KEY=bth_live_…
npm run theme -- submit my-theme --changelog "First release"

Your version lands as Pending and appears on your dashboard. The Baseet team reviews it, then publishes it or returns notes. Once published, it renders live for merchants with no deploy.

You've done the full loop: build → validate → submit → review → publish. Everything else in these docs makes each step better.

Core concepts

A few ideas explain every decision on the platform. Learn these and the rest follows.

Data, not code

A Baseet theme ships no executable code. It's a manifest (structure) plus a preset of design tokens (look). One trusted renderer turns that data into a storefront. This is the platform's core safety property: the least-privileged thing you can build is a theme, so themes are safe to install, review, and run at scale — and a theme can go live without a code deploy.

Two planes

PlaneWhat
baseet.ccThe product — merchants, storefronts, the company. Ships fast.
baseet.devThe platform — you, the docs, the APIs, the tools. Moves on stable, versioned contracts.

They're deliberately separate: a product change can never break a contract you built against.

Stable contracts

Everything you depend on — the theme manifest schema, the CLI, and (soon) the API and webhooks — is a versioned contract. Changes are additive by default; anything removed is deprecated on a published schedule with a migration guide. See Versioning & stability.

Least-privilege credentials

Your key is scoped to exactly one capability and nothing else. It can't reach billing, other developers' themes, or any other data. A leaked key's worst case is "a theme that gets rejected in review." See Security.

Theme anatomy

A theme is exactly two files of data, plus the one engine that renders them.

The manifest — structure

Declares the theme's identity and its ordered list of sections (hero, categories, featured products, testimonials, …), each with a layout variant and animation. The manifest defines availability + order; every section still gates on the merchant's own store config at render time.

export const MyThemeManifest = {
  id: 'my-theme',
  name: { en: 'My Theme', ar: 'ثيمي' },
  version: '1.0.0',
  industry: 'fashion',
  directions: ['rtl', 'ltr'],
  sections: [
    { schemaId: 'hero', layoutVariant: 'split', animation: 'fadeUp' },
    { schemaId: 'categories', animation: 'fadeUp' },
    { schemaId: 'featured', animation: 'staggerCards' },
    // …reorder / remove / re-variant freely
  ],
};

The preset — look + starter content

Design tokens (colors, radii, fonts) plus demo hero/benefits content. The theme's entire visual language lives in tokenOverrides.

export const MY_THEME_PRESET = {
  id: 'my-theme', themeId: 'my-theme',
  accentColor: '#1C1917',
  tokenOverrides: {
    '--color-background': '#FAFAF9',
    '--color-accent': '#1C1917',
    '--radius-card': '14px',
    '--font-display': "'Fraunces', serif",
    '--font-body': "'Manrope', 'IBM Plex Sans Arabic', sans-serif",
  },
  homeContent: { /* demo hero, benefits, … */ },
};

The engine — one renderer for every theme

The storefront engine reads your manifest + tokens and dispatches each section to the shared component registry. You never write render code — which is why a new theme is a manifest + a preset, and nothing else.

Authoring guide

The end-to-end process for building a theme that's distinct, correct, and publishable.

Step 1 — Brief before pixels

Write one page: target merchant (fashion / food / beauty / electronics / home), three real-brand inspirations (not other themes), a one-sentence intent, and your density × shape × tone choice. A theme that doesn't move at least two of those axes vs. existing themes is "minor variant noise."

Step 2 — Tokens before layout

Design the token map first — color roles, radius scale, shadow vocabulary, and the type pair (verify it covers Latin + Arabic + numerals). The shadow scale is often the most differentiating dimension: sharp themes have none, soft themes layer them.

Step 3 — Compose sections

Pick the section order and variants in the manifest. There's no new code — choosing the right defaults per section is the only decision. Alternate section weights so the page has rhythm; never place two hero-weight sections back to back.

Step 4 — Validate & preview

Run npm run theme -- validate and view it on a demo store in both EN/LTR and AR/RTL, mobile and desktop. See Testing & preview.

Step 5 — Pass the design bar, then submit

Score against the design bar (anti-AI checklist ≤ 2 hits), then submit.

💡The shortcut that destroys themes is starting in code. Brief → tokens → compose → validate. In that order.

Sections & tokens

The building blocks you compose, and the token vocabulary that colors them.

Sections

Each manifest entry references a schemaId. The engine renders it from the merchant's store data. Common sections:

schemaIdRenders
heroHero banner / slider (variants: split, static, full)
categoriesCategory tiles (grid / pills / circles / scroll)
featured · new_arrivals · products · most_loved · saleProduct grids
benefitsTrust/benefit strip
banners · promoPromotional banners
testimonials · brands · instagramSocial proof
bundles · bundle_offers · shop_the_look · before_after · hero_productMerchandising
💡The full, always-current section list is generated from the engine schema — see the Sections & fields reference, so it can never drift from what the engine actually renders.

Design tokens

Sections read CSS variables, never raw colors — so your preset's tokens re-skin every section at once. Token names are roles, not adjectives (--color-accent, not --color-blue). Core roles:

  • Color--color-background, --color-surface, --color-on-surface, --color-accent, --color-on-accent, --color-muted, status roles.
  • Radius--radius-card, --radius-button, --radius-input.
  • Type--font-display, --font-body, and always an Arabic-capable stack.

The design bar

Every theme must clear this standard before it publishes. It's what keeps the marketplace worth browsing.

Engineering rules (must)

  • Never hardcode a color, radius, or shadow in a section — read a token.
  • A theme is data, never React. If you want to write JSX, you've found a missing section, not a theme.
  • Every theme ships every token the schema requires.
  • Token names are roles, not adjectives.

Design rules (the taste bar)

  • A distinct design language across all surfaces — home, product, checkout, profile, empty/error states — not "the home page styled."
  • One icon family, one stroke weight, one fill style.
  • Mobile and desktop are equal first-class targets; touch targets ≥ 44px.
  • WCAG AA contrast (4.5:1 body); focus rings always visible; color never the only signal.

The anti-AI checklist — block at 3+ hits

Score your theme against these; three or more and it won't pass review:

  1. Indigo/purple gradient as the primary accent.
  2. 3D blobs or floating geometric shapes in the hero.
  3. Sparkle / wand / star icons in a storefront section.
  4. "Get Started Free →" or "Modern. Fast. Beautiful." copy.
  5. Cards rotated "for energy."
  6. Every card using the same hover:-translate-y-1 hover:shadow-2xl.
  7. Tailwind default slate greys as the only surface tone.
  8. Aurora / mesh-gradient backgrounds without purpose.
  9. Glassmorphism as the default surface.
  10. Stock Unsplash placeholder photography.
The benchmark is themes drawn by humans with taste. "Different colors on the same skeleton" is not a new theme.

RTL & Arabic

Baseet is Arabic-first. RTL is a first-class design target, not a mirror of the LTR version.

  • Design in EN (LTR) and AR (RTL) simultaneously — flipping later reliably breaks layouts.
  • Ship --font-arabic separately — a Latin font's fallback silently covers Arabic with the wrong glyphs. Lead Arabic stacks with the Arabic font.
  • Arabic body copy usually needs +0.1–0.2 line-height vs. Latin.
  • Directional icons (chevrons, arrows) flip automatically; brand and object icons must not.
  • Numerals, prices, phone numbers, SKUs, and codes stay LTR inside RTL text.
  • Use logical CSS (padding-inline-start, not padding-left). Note: logical props resolve per-element direction — inside a dir="ltr" island (a price, a phone input) they don't flip with the page.
💡Native-feeling Arabic is Baseet's edge over every competitor running on generic templates. Treat it as the main event.

Testing & preview

Prove the theme works before you submit — the review is faster when you've already checked.

Validate

npm run theme -- validate my-theme

Checks bilingual names, RTL support, that every section resolves in the schema, known field types, and that your preset and manifest are wired consistently.

Preview on a demo store

Point a demo store's theme_id at your theme id and open it. Check, in this order: home, product listing, product detail (with several sections on), cart, checkout, thank-you, profile, and the empty / error / loading states — each in EN + AR, mobile + desktop.

The pre-submit checklist

  • Header/footer identical on every page.
  • Same button, input, and modal style everywhere.
  • Long product page has rhythm (alternating section weights).
  • Empty states are designed, not blank.
  • Anti-AI checklist ≤ 2 hits.

Submit & review

How a theme goes from your machine to the review queue, and what reviewers look for.

Submitting

export BASEET_THEME_KEY=bth_live_…
npm run theme -- submit my-theme --changelog "what changed"

The CLI validates locally first, then submits the manifest + tokens to Baseet over an authenticated, scoped-key endpoint. The version lands as Pending and shows on your dashboard. Each release is an immutable version — to iterate, bump the semver and resubmit.

Your key is the only credential the CLI uses. Export it at submit time; never commit it or paste it anywhere public.

What review checks

  • Design quality — the design bar (distinct language, anti-AI ≤ 2).
  • RTL / Arabic correctness — native-feeling in both directions.
  • Accessibility — contrast, focus, touch targets.

Outcomes

A reviewer approves (→ ready to publish), requests changes (with specific notes on your dashboard — fix and resubmit a new version), or rejects. You see every decision and note in the Theme detail view.

Publishing

What happens when your theme goes live — and the promise that comes with it.

Once a reviewer publishes an approved version, it enters the Baseet theme library and any merchant can install it. Because a theme is data, publishing takes effect with no deploy — the storefront engine picks it up from the registry.

Lifecycle states

StateMeaning
DraftCreated, not yet submitted.
In reviewA version is pending a decision.
PublishedLive in the library, installable.
SuspendedTemporarily removed (contact the team).
RetiredWithdrawn from new installs.

Your dashboard shows install/usage analytics per theme so you can see your reach.

Versioning a theme

Ship improvements without breaking merchants already using your theme.

  • Every submission carries a semver (x.y.z) and is immutable once submitted.
  • Patch (1.0.1) — safe, additive fixes.
  • Minor (1.1.0) — new sections/options, backwards-compatible.
  • Major (2.0.0) — a redesign that changes the look meaningfully; treat it as a distinct offering so merchants opt in deliberately.

The manifest also declares the schema version it targets. Baseet keeps published themes rendering across schema changes; when a required capability changes, you'll get a deprecation window and a migration note — see Versioning & stability.

CLI reference

The theme developer kit. One tool: scaffold, validate, list, submit.

init <id>

Scaffolds the manifest + preset for a new engine theme and auto-registers it.

npm run theme -- init <id> [--name-en X] [--name-ar X] [--industry fashion] [--accent #hex]

validate [<id>]

Runs the acceptance gate: bilingual names, RTL, sections resolve, field types, preset/manifest consistency. Add --all to validate every theme.

npm run theme -- validate <id>

list

Lists every registered theme and whether it's engine-enabled.

npm run theme -- list

submit <id>

Validates locally, then submits the theme for review. Requires your key.

export BASEET_THEME_KEY=bth_live_…
npm run theme -- submit <id> [--changelog "…"]
Env varPurpose
BASEET_THEME_KEYYour scoped developer key (bth_live_…). Never stored or committed.
BASEET_THEME_SUBMIT_URLOptional override of the submit host.
💡This reference will be generated from the CLI's own definitions so it always matches the tool exactly.

Tutorial — sync a catalog to an ERP

A complete walkthrough: read a store's catalog, keep it in sync, and react to changes. ~15 minutes. All code is Node 18+ (built-in fetch) or the SDK.

1 · Get a key with the right scopes

The merchant creates a key in Baseet dashboard → Settings → API Keys. For a two-way ERP sync, grant read_products + write_products (add read_store to read the currency). Store it as BASEET_KEY.

2 · Read the whole catalog

Lists are cursor-paginated — loop until next_cursor is null:

const BASE = "https://api.baseet.dev/2026-07";
const H = { Authorization: `Bearer ${process.env.BASEET_KEY}`, "Baseet-Version": "2026-07" };

async function* allProducts() {
  let cursor = null;
  do {
    const qs = new URLSearchParams({ limit: "100" });
    if (cursor) qs.set("cursor", cursor);
    const res = await fetch(`${BASE}/products?${qs}`, { headers: H });
    if (!res.ok) throw new Error(`${res.status} ${(await res.json()).error?.message}`);
    const page = await res.json();
    for (const p of page.data) yield p;   // p.price.amount is MINOR units
    cursor = page.next_cursor;
  } while (cursor);
}

for await (const p of allProducts()) upsertIntoErp(p);

3 · Map the fields

  • Money: price.amount is an integer in minor units — 45000 ÷ 100 = 45.00 price.currency.
  • Names: title.en / title.ar — pick per your ERP's locale.
  • Identity: store Baseet's id against your ERP row so later updates target the right product.

4 · Push a change back (idempotent)

When the ERP changes a price or stock, PATCH it. Always send an Idempotency-Key so a network retry can't double-apply:

await fetch(`${BASE}/products/${baseetId}`, {
  method: "PATCH",
  headers: { ...H, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
  body: JSON.stringify({ price: { amount: 39000, currency: "EGP" }, status: "active" }),
});

5 · Create products from the ERP

await fetch(`${BASE}/products`, {
  method: "POST",
  headers: { ...H, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
  body: JSON.stringify({ title: { en: "New SKU", ar: "منتج" },
    price: { amount: 25000, currency: "EGP" }, status: "draft", sku: "ERP-1001" }),
});
A 400 "create a product category first" means the store has no category yet — create one in the dashboard, then retry.

6 · Stay in sync — webhooks, not polling

Instead of re-reading everything, subscribe to products/updated + orders/created and update the ERP on each event. Verify the signature before trusting a payload (see Webhooks below / the Authentication page). That's a two-way sync: ERP → Baseet via writes, Baseet → ERP via webhooks.

Next: the Examples for copy-paste recipes, or the SDK to skip the fetch boilerplate.

Guides

Task-focused how-tos. Start with the Quick start, then reach for these.

GuideCovers
API overviewbase URL, versioning, conventions (money, locales, pagination)
Authenticationkeys, scopes, rate limits, errors, idempotency
SDKthe typed @baseet/sdk client
Catalog ↔ ERP syncthe full worked walkthrough
Examplesshort copy-paste recipes
Troubleshootingerror codes and fixes

Building a theme instead of integrating? See the theme docs (quick-start router) — that's a different track.

Examples

Short recipes. All assume BASE + H from the tutorial.

Find a product by SKU

// no server-side SKU filter yet — page + match client-side
let hit = null;
for await (const p of allProducts()) if (p.sku === "WAL-01") { hit = p; break; }

Bulk price change (+10%)

for await (const p of allProducts()) {
  const amount = Math.round(p.price.amount * 1.1);
  await fetch(`${BASE}/products/${p.id}`, { method: "PATCH",
    headers: { ...H, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
    body: JSON.stringify({ price: { amount, currency: p.price.currency } }) });
}

Export the catalog to CSV

const rows = [["id","title","price","currency","status"]];
for await (const p of allProducts())
  rows.push([p.id, p.title.en, (p.price.amount/100).toFixed(2), p.price.currency, p.status]);
require("fs").writeFileSync("catalog.csv", rows.map(r => r.join(",")).join("\n"));

Verify a webhook (Express)

import { createHmac, timingSafeEqual } from "node:crypto";
app.post("/hook", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.get("X-Baseet-Hmac-SHA256") || "";
  const exp = createHmac("sha256", process.env.HOOK_SECRET).update(req.body).digest("hex");
  const a = Buffer.from(exp), b = Buffer.from(sig);
  if (a.length !== b.length || !timingSafeEqual(a, b)) return res.sendStatus(401);
  res.sendStatus(200); // then process JSON.parse(req.body) async, de-dupe on event id
});

Migration guides

How to move between API versions when the time comes.

Today

There is one live version — 2026-07. Nothing to migrate yet. Pin it with Baseet-Version: 2026-07 and you're stable.

When a new version ships

  • The old version keeps working for ≥12 months — no rush.
  • A migration note lists exactly what changed (added/removed/retyped fields) and the replacement.
  • Move at your own pace: change the Baseet-Version header, adjust for the listed diffs, test, ship.
  • Within a version nothing breaks (additive only), so day-to-day you never migrate.

See the Deprecation policy for the guarantees.

Themes

A theme version is data (the manifest). A store re-resolves to a newer theme version with its configuration preserved — there's no per-store code migration.

Release notes

2026-07 — initial public API

  • API launched at api.baseet.dev/2026-07: GET /store, GET /products (cursor pagination + filters), GET /products/{id}, POST /products, PATCH /products/{id}.
  • Auth: scoped bak_live_ keys (per-store, hashed, revocable) with read/write scopes.
  • Writes are replay-safe via Idempotency-Key.
  • Webhooks contract: signed (X-Baseet-Hmac-SHA256), versioned topics for products/orders/customers/inventory.
  • SDK: @baseet/sdk, typed from the same contract.

Full change history: Changelog. Guarantees: Versioning & stability.

API — Overview

Connect an external system — an ERP, a PIM, a custom app — to a store's data over versioned REST.

Base URL: https://api.baseet.dev/2026-07. Send the pinned version on every request as Baseet-Version: 2026-07 — a dated version is an immutable contract; breaking changes ship as a new dated version.

Your first call

curl "https://api.baseet.dev/2026-07/store" \
  -H "Authorization: Bearer bak_live_xxx" \
  -H "Baseet-Version: 2026-07"

Conventions

  • Money is an integer in minor units + currency: { "amount": 110000, "currency": "EGP" } = 1100.00 EGP.
  • Text is bilingual: { "en": "…", "ar": "…" }.
  • Lists are cursor-paginated — follow next_cursor until it is null.
🔑Get a key: the store owner creates one in Baseet dashboard → Settings → API Keys, choosing least-privilege scopes. It's shown once.

API — Authentication

Every request carries a scoped bearer key: Authorization: Bearer bak_live_…

A key belongs to one store — resolved from the key, so you never pass a store id, and a key can never touch another store. Keys are stored hashed and are revocable (a revoked key returns 401 on the next request).

Scopes

Reads and writes are separate; grant only what's needed:

read_store read_products write_products read_orders write_orders read_customers write_customers read_inventory write_inventory read_discounts write_discounts

A call missing its scope returns 403 forbidden.

Rate limits

120 requests/minute per key. Responses carry RateLimit-Limit/Remaining/Reset; on exhaustion, 429 with Retry-After.

Errors

One provider-agnostic envelope: { "error": { "type", "message", "field", "request_id" } }. Types: invalid_request · unauthorized · forbidden · not_found · conflict · rate_limited · server_error. Log request_id for support.

Idempotency

Writes accept an Idempotency-Key (a UUID). A retry with the same key returns the original result; reuse with a different body returns 409. Always send one on writes.

API endpoints

The live endpoints — generated from the OpenAPI contract (openapi.ts), so this list can never drift from what the API actually serves.

Loading endpoints…

SDK — @baseet/sdk

A typed client for the Baseet API. Its types are reused from the API contract, so the SDK can't drift from what the API returns.

Install & init

import { Baseet } from "@baseet/sdk";
const baseet = new Baseet({ key: process.env.BASEET_KEY, version: "2026-07" });

Read the store & catalog

const store = await baseet.store.get();
// products.list() is an async iterator over the WHOLE catalog (cursor pagination handled):
for await (const p of baseet.products.list({ status: "active" })) {
  console.log(p.id, p.title.en, p.price.amount, p.price.currency); // amount = minor units
}
const one = await baseet.products.get("prod_1a2b");

Write (idempotent)

const created = await baseet.products.create(
  { title: { en: "New", ar: "جديد" }, price: { amount: 25000, currency: store.currency }, status: "draft" },
  { idempotencyKey: crypto.randomUUID() },   // safe to retry
);
await baseet.products.update(created.id, { status: "active" }, { idempotencyKey: crypto.randomUUID() });

Errors

Failures throw a BaseetError mirroring the API envelope — type, status, request_id, field. Ships versioned in lockstep with the API. Thin over fetch; inject one in non-global-fetch runtimes.

Glossary

TermMeaning
StoreOne merchant's shop. The unit a key is scoped to.
bak_live_ keyA merchant's scoped API key — read/write their own store's data (integrations, ERP).
bth_live_ keyA developer's key — build & submit themes for many stores. Not a store key.
ScopeA permission a key holds, e.g. read_products. Least-privilege; read ≠ write.
Version (dated)An immutable API contract like 2026-07. Breaking changes ship as a new dated version.
Minor unitsMoney as an integer in the smallest unit: 110000 = 1100.00. Never a decimal.
CursorOpaque pagination token; follow next_cursor until null.
Idempotency keyA UUID on a write; a retry with the same key returns the original result.
Webhook topicAn event name like products/updated you subscribe to.
Bespoke themeA hand-built React theme, gated on theme_id.
Engine themeA data-not-code theme (manifest + preset) rendered by one component.
ManifestThe data that declares a theme's sections & fields — edited by merchants, published as data.
PresetA theme's design tokens (color, type, radii, motion).
SectionA composable storefront block (hero, collections…) with editable fields.

FAQ

How do I get an API key?

A merchant creates one in the Baseet dashboard → Settings → API Keys, choosing scopes. It's shown once.

Is the API read-only?

No — with write_products you can create/update products. Reads and writes are separate scopes.

Why are prices huge numbers?

Money is in minor units: 110000 = 1100.00. Divide by 100 (or the currency's exponent) to display.

How do I paginate?

Follow next_cursor until it's null, or use the SDK's products.list() async iterator.

How do I avoid duplicate writes on retry?

Send an Idempotency-Key (UUID) on every write.

Can one key access more than one store?

No. A key is bound to exactly one store; the store is resolved from the key.

How do I build a theme?

See the theme docs (start with the quick-start router). Developers use a bth_live_ key + the theme CLI.

Troubleshooting

SymptomCause & fix
401 unauthorizedMissing/malformed/revoked key. Check the Authorization: Bearer bak_live_… header; mint a new key if revoked.
403 forbiddenThe key lacks the scope. Add the scope (e.g. write_products) when creating the key.
400 invalid_request on createMissing title.en, bad price, or the store has no product category — create a category first.
409 conflictAn Idempotency-Key was reused with a different body. Use a fresh UUID per distinct write.
429 rate_limited120 req/min exceeded. Honor Retry-After and back off.
Empty product titlesYou're reading a raw field — use the API's title.{en,ar}, not internal columns.
Webhook payload can't be trustedVerify the X-Baseet-Hmac-SHA256 signature over the raw body before parsing.
Arabic renders in a Latin fontLead the Arabic-primary font stack with the Arabic face (Latin fallbacks silently cover Arabic).

Still stuck? Include the request_id from the error envelope when you contact support.

Deprecation policy

Everything you depend on is a versioned contract. We change it predictably.

  • Additive by default. Within a dated version (2026-07), fields are only added — never removed or retyped.
  • Breaking changes = a new version. A removed/renamed field or tightened validation ships as a new dated version, never in place.
  • Support window. When a new version ships, the previous one is supported for ≥12 months.
  • Notice. Deprecations are announced with the version that introduces the replacement and a migration note.
  • Themes follow the same rule via the manifest: a store re-resolves to a newer version with its config preserved.

See also Versioning & stability.

Sections & fields reference

The complete section vocabulary the engine renders — generated from the code (SECTION_SCHEMA), so it can never drift from what actually ships.

Loading reference…

Versioning & stability

The promise that lets you build on Baseet without fear of it moving under you.

  • Additive by default. New capability = a new field / option / version. Existing fields don't change meaning or disappear inside a version.
  • Deprecation is scheduled, never silent. Anything going away is announced on the changelog with a migration guide and a support window (target: ≥ 12 months) before removal.
  • Contracts are versioned. The theme manifest schema, the CLI, and — soon — the public API and webhooks each carry a version you can pin against.
  • Calendar versioning for the platform API (dated, e.g. 2026-07): a version you adopt never changes underneath you.
💡The rule underneath all of it: a Baseet product change can never break a published contract. That's the entire point of the two-plane split.

Security

The model that makes building on Baseet safe for you and for merchants.

Your key

  • Scoped to one capability (submit your own themes) — nothing else. It cannot reach billing, tenants, or other developers' data.
  • Stored hashed — the raw value is shown once. Rotate or revoke it any time from the dashboard.
  • A leaked key's worst case is a theme that gets rejected in review.

Why data-not-code is a security feature

A theme carries no executable code and no secrets — it's the least-privileged thing on the platform. That's what lets Baseet review, install, and run third-party themes across thousands of stores with confidence.

Your responsibilities

  • Never commit or expose your key.
  • Never ship placeholder secrets or third-party tokens inside a theme.
  • Report anything that looks like a way to exceed a theme's privileges.

Changelog

Every change to the developer platform, dated and categorized. This page is a contract — nothing changes without appearing here.

2026-07 · Developer platform — early access

  • Added — the theme marketplace: scoped developer keys, CLI submit, the review queue, and publish-with-no-deploy.
  • Added — the developer dashboard at developers.baseet.dev.
  • Added — these docs at docs.baseet.dev.

Roadmap

What we're building, roughly in order. Directional, not a commitment on dates.

Now

  • Theme platform to GA — the developer dashboard, submission, review, and publish loop.

Next

  • Generated reference — manifest, section, and CLI reference generated from the code schemas so they can't drift.
  • The public API — a versioned REST API + a typed SDK, designed so the same schema powers the SDK and AI/MCP tools.
  • Webhooks — a signed, versioned event contract.

Later

  • Apps & extensions · a partner program · earnings statements & payouts.