Developers
Start building
Apps/Build your first app

Build your first app

Scaffold an app with the Poltura CLI, change its storefront block, build and validate it, and publish the first release for review. About fifteen minutes.

On this page

You will build a hosted app with two surfaces: a page in the merchant's admin and a block merchants can place on their storefront home page. At the end you publish its first release, which goes to review before any store can install it.

Do this tutorial with an AI coding toolPaste into your AI coding tool. It points the tool at these docs.

Build a Poltura app by following https://developers.poltura.com/apps/build-your-first-app.md step by step, and keep to the rules in https://developers.poltura.com/ai/rules/AGENTS.md. Scaffold it with poltura app create my-app --template hosted and install its dependencies. Change the storefront block in src/elements.ts and src/workers/storefront-home.tsx as the tutorial shows. Run poltura app build and poltura app validate and fix every error until validate prints "ok". Do not publish; stop and tell me the app is ready for poltura app publish.

Before you start

  • A Poltura store where you are an administrator.
  • Developer mode on. In the admin, open Settings → Profile and turn on Developer mode. Developer apps appears in the sidebar.
  • An organization and an app. In Developer apps, choose Create organization, then Create app. Note the app id, a UUID such as 0199f3a2-5b1c-7d4e-9f20-3a4b5c6d7e8f.
  • A developer token. On the same page, under Developer tokens, choose Create token and confirm it's you when asked. A token the organization owns starts with pltd_ and suits CI; one only you own starts with pltu_. Tokens last 30 days by default; you can pick 7 days, 90 days, 1 year or no expiry. The token is shown once. Keep it in an environment variable:
export POLTURA_APP_TOKEN="pltd_..."
  • Node.js 22 or later.

Install the CLI

Not on npm yet
$ npm install -g @poltura/cli

Check that it runs:

poltura --help

Create the app

poltura app create my-app --template hosted
created hosted app in /Users/you/my-app

--template native writes the UI workers only. --template hosted also writes src/server.ts, a small Node server: /connect starts the connection, /oauth/callback finishes it and reads the store, and /webhooks/products receives signed webhooks. The directory must be empty or not exist yet.

poltura.app.json
package.json
tsconfig.json
README.md
elements.ts
server.ts
styles.css
admin-page.tsx
storefront-home.tsx

Install the dependencies

$ cd my-app
$ npm install

The build needs react, react-dom, @poltura/app-ui and tailwindcss in the project. Without them it stops with Install react in this project to build workers.

Point the app at your server and your app id

poltura.app.json is the app's manifest. The scaffold uses https://app.example.com for every address; change them to the HTTPS origin your server will run on, and add the app id from Developer apps so publishing knows which app the release belongs to:

poltura.app.json (excerpt)
{
  "schemaVersion": 1,
  "apiVersion": "v1",
  "appId": "0199f3a2-5b1c-7d4e-9f20-3a4b5c6d7e8f", 
  "handle": "my-app",
  "releaseVersion": "1.0.0",
  "displayName": "My App",
  "backendOrigin": "https://app.example.com", 
  "backendOrigin": "https://my-app.example.org", 
  "redirectUrls": ["https://app.example.com/oauth/callback"], 
  "redirectUrls": ["https://my-app.example.org/oauth/callback"], 
}

Change supportUrl, privacyUrl, homepageUrl and the webhook and destination URLs the same way. The handle must be the Handle you gave the app in Developer apps.

The permissions block lists what the app reads and writes. Every permission carries a purpose of 8 to 280 characters, and merchants read it on the install screen before they approve:

poltura.app.json
"permissions": {
  "required": [
    { "id": "settings.read", "purpose": "Read store locale, currency, and public capabilities" },
    { "id": "products.read", "purpose": "List products and collections for the companion admin page" }
  ],
  "optional": []
}

Keep only what the app uses. Installation storage needs no permission. Every permission is listed in Permissions and topics.

Change the storefront block

The home block is declared once in src/elements.ts. Its settings become fields merchants edit in the customizer, and its style list decides which style controls they get:

src/elements.ts
export const catalogHighlight = defineBlock({
  type: 'catalog-highlight',
  label: 'Catalog highlight',
  description: 'A short catalogue note on the home page.',
  icon: 'sparkles',
  category: 'marketing',
  settings: {
    title: field.text({ label: 'Heading', default: 'Catalog companion' }), 
    title: field.text({ label: 'Heading', default: 'New this week' }), 
    note: field.text({ label: 'Note', default: 'Fresh picks from the catalogue.' }), 
    show_note: field.checkbox({ label: 'Show note', default: true }),
  },
  style: ['spacing', 'background', 'border'],
});

The worker draws it with the storefront primitives, so it follows the store's theme:

src/workers/storefront-home.tsx
function Home(): ReactNode {
  const settings = useSettings();
  return (
    <Card className='rounded-theme bg-surface text-foreground p-4'>
      <Stack>
        <Heading>{String(settings.title ?? 'Catalog companion')}</Heading>
        {settings.show_note === false ? null : <Text>Storefront home block for this Poltura app.</Text>} {}
        {settings.show_note === false ? null : <Text>{String(settings.note ?? '')}</Text>} {}
      </Stack>
    </Card>
  );
}

Check what the app now defines:

poltura app elements
catalog-highlight	Catalog highlight	3 settings

Build and validate

poltura app build
poltura app validate
built my-app@1.0.0  sha256-dfw5wNzsE0rCJlRiYs68FfVqGAQWsH+I6tStN34IgYI=  2 workers
built my-app@1.0.0  sha256-dfw5wNzsE0rCJlRiYs68FfVqGAQWsH+I6tStN34IgYI=  2 workers
ok my-app@1.0.0
  manifest.json  3906 bytes

Your digest and size differ. build bundles each worker, compiles src/styles.css and writes the release to dist/ (--out picks another directory). validate builds into a temporary directory and checks the manifest the review will see. While you edit, poltura app dev rebuilds on every change and prints watch <digest> when the release changes.

Publish for review

poltura app publish --api "$POLTURA_API" --token "$POLTURA_APP_TOKEN"
published 0199f3b0-1e2d-7a3c-8b4f-5c6d7e8f9a0b

--api is the Poltura API origin and is required; setting POLTURA_API works too. Its production address is not published yet (see Authentication). Add --admin-origin <your admin URL> and the CLI also prints the review link. If you skipped appId in the manifest, pass --app <app id>; without it the CLI stops with missing --app <appId>.

Review and install

A Poltura reviewer who is not in your organization checks the exact release you uploaded. Once it is approved, a store can install it:

  1. Run src/server.ts on your HTTPS origin with the variables its README lists, and npm start.
  2. Open https://my-app.example.org/connect?installation=<installation id> as the store's administrator. Poltura shows the permissions with your purposes; approve, and the server answers Connected to <store name>.

The app's admin page and home block are now in that store, and merchants place the block in the Customizer like any other. Everything after publishing is on Ship to merchants.

Next steps

Last updated