# Building an extension > Third-party submission does not exist yet (M11). Today this page documents how **first-party** extensions are laid out in the monorepo — the exact contract third parties will get. A first-party extension has three homes: 1. **Manifest + config schema** — an entry in `packages/core/src/extensions/registry.ts`: `buildManifest({...}, configZod)` attaches the generated JSON Schema. The Zod is the single source of truth for config validation; the manifest is what the platform and this site read. 2. **Runtime module** (only if the extension has code) — `apps/api/src/extensions/runtime.ts` maps the extension id to its `ExtensionRuntime`: `payment` / `cargo` provider implementations, `orderInstructions()`, and (via `apps/api/src/extensions/jobs.ts`) job handlers named in `manifest.jobs`. Provider code receives a `ProviderCtx` with **typed `secrets`** — exactly the manifest-declared keys — and `config`; it never reads the database or the request. 3. **Admin UI module** (only if a panel or action needs code) — `apps/admin/src/extensions/{name}/index.tsx` default-exports `{ panels: { [panelId]: Component }, actions: { [actionId]: Modal } }`; zone hosts lazy-load it only when the manifest declares a panel for that zone. Extension text is module-local — it never enters the shared panel dictionary. Everything else — settings forms, secret fields, enable/disable, ordering, the consent screen, webhook ingestion, job scheduling, CSP origins — is rendered or dispatched by the platform from the manifest. ## Checklist - Declare only the `scopes` you need; they appear on the consent screen verbatim. Declare `pii.buyer` honestly — undeclared fields never reach your code. - Keep your config schema inside the renderable subset (booleans, numbers/money, strings/enums, one-level objects, arrays of flat objects, arrays of scalars). A registry test rejects anything the form generator cannot render. - Mail contributions must be promo-free (`promoFree: true` is an attestation AND a runtime lint). - Respect the [limits](/reference/limits). Hooks fail open and trip a breaker; don't rely on being called. - Never name a provider id in core code paths. If you need the platform to know something about your extension, put it in the manifest.