Deploy
Prepare credentials, origins, redirects, and verification for the framework and host you choose.
Deploy
You host your own app. 01.software is the backend it talks to, so your framework and host own deployment while this guide defines the platform-specific handoff.
01.software does not choose or run your frontend framework, hosting provider, build, or release process. Use your framework and host documentation for those steps. This guide owns only the 01.software handoff around credentials, browser origins, customer redirects, and separate verification.
Common deployment handoff
Use this section for every path in Build Options. The deployment command, environment-variable syntax, build output, and release mechanism remain owned by the application runtime and hosting provider you choose.
| Build Options path | Continue with |
|---|---|
| New opinionated Next.js app | Use the scoped Next.js procedure below with any host that runs the app. It is path-specific guidance, not the common contract for every build option. |
| Existing supported JavaScript or TypeScript project | Use that project's framework and host documentation, then apply the common credential, origin, redirect, and verification requirements below. Use the Next.js procedure only when the project is Next.js. |
| Existing Astro, Remix, SvelteKit, or another meta-framework | Use the manual init output plus that framework's environment and deployment documentation. Do not translate the Next.js variable names below into a claim that they are portable. |
| Runtime where the TypeScript SDK does not fit | Use the runtime and host's deployment process for the direct HTTP path, and follow each OpenAPI operation's declared authentication. No framework adapter or hosted runtime is supplied by 01.software. |
For every path:
- Keep deployed application credentials separate from local development
credentials. A Publishable Key may be deployed in browser code. Issue and
store a separate production Secret Key only when the workload performs
privileged server-side SDK, CLI, or API operations; keep it in server-side
runtime configuration or the host's secret store. Do not issue an
sk01_solely for a browser-only workload or one that calls only public operations. A Human CLIpat01_is never an application credential. - If the deployed app makes browser calls, add each deployed browser origin to
the workspace allowlist. Server-only calls have no browser
Origin; their success does not prove the browser allowlist is correct. - If the app uses hosted customer OAuth, update its
customer-oauth-clientsrow. Register at least one exact browser Origin inallowedOrigins, at least one exact callback URI inallowedRedirectUris, and every post-logout URI the app uses inallowedLogoutUris. The OAuth origin list is checked for browser token and refresh requests when anOriginheader is present; a missing header is allowed for non-browser or server requests. These exact OAuth client registries do not replace the workspace Browser API origins allowlist from item 2, and the origin, redirect, and logout entries do not replace one another. - Deploy with the framework and host's own process. Verify server credential reachability, browser-origin admission, and customer sign-in separately when each applies. One passing check does not prove the others, and none records a deployed state or proves launch readiness.
Next.js template procedure
The detailed procedure below applies only to the currently registered Next.js starters and existing Next.js projects that use the same environment-variable conventions. Vercel is one example host. For every other Build Options path, keep the common handoff above and follow that runtime's own documentation.
Issue deployment credentials separately
Do not copy your local .env.local into your host. It can contain a
development sk01_ with a different rotation, audit, and compromise boundary.
Only workloads that make server-side SDK or CLI calls need a production
sk01_.
Initial production provisioning
For the first production deployment of such a workload, issue a production key in Console and put its one-time reveal in the host's server-only secret setting. No deployment or restart belongs in this credential-preparation step: configure the host here, then use the Deploy step below for the one initial deployment and its initial-key verification. The secret is shown only once; if you lose it, create a new key instead of trying to recover it.
Rotate an already deployed workload
For a workload that is already deployed, issue a replacement in Console, copy its one-time reveal into the host's server-only secret setting, update the host to use that replacement, then deploy or restart the workload. Verify the workload works with the replacement key before you revoke the old key in Console.
The Community Forum template uses a publishable key and customer JWTs, not a
template-side server secret. Do not issue or configure a production sk01_
solely for that secret-free workload.
These names apply to the Next.js template covered by this page. Other template families use their own public publishable-key convention. Keep the production secret in the host's server-only secret setting only when the workload makes server-side SDK or CLI calls:
| Variable | Where it ends up |
|---|---|
NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY | the client bundle — this is intended and safe |
SOFTWARE_SECRET_KEY | server only |
| checkout secrets and mode/market values | server only |
The rule is the prefix: in a Next.js app, NEXT_PUBLIC_ is what gets inlined
into browser code. Anything without that prefix stays on the server. Never put
an sk01_ or another secret in a NEXT_PUBLIC_, VITE_, or REACT_APP_
variable, a command-line argument, a URL, or source control.
Never add NEXT_PUBLIC_ to the secret key to "make it work" in a component
that runs in the browser. If a component needs data the publishable key cannot
read, move that call to a server component or route handler.
Allow the deployed origin
Browser calls are refused unless the calling origin is on your workspace allowlist. Add your deployed URL under Browser API origins on your workspace record in Console.
https://your-app.example.comExact origins and one level of wildcard subdomain are accepted:
https://example.com exact
https://*.example.com one level of subdomain
http://localhost:3000 local developmentPreview deployments get their own generated URLs. Add the pattern that covers them, or the previews will fail while production works.
An empty allowlist blocks every browser call — the list is fail-closed, not
permissive-by-default. If you forget this step the deploy looks broken in a
confusing way: server-rendered pages work (server-to-server requests send no
Origin), and only browser calls fail.
Symptom when the origin is missing: the API responds 403 with reason
origin_not_allowed — but you cannot see that reason from the browser. The
403 carries no Access-Control-Allow-Origin, so the browser withholds the
response from JavaScript and leaves a generic CORS error in the console.
The preflight will not warn you first. A CORS preflight announces header
names and carries none of their values, so it arrives without your
publishable key, takes the unauthenticated path, and is answered 200 with
Access-Control-Allow-Origin: * — from any origin, allowed or not. The
rejection lands on the real request that follows.
To see the reason, send the probe below. It is not what the browser sends: the
key makes it take the tenant path, which reaches the CORS decision before any
per-collection access check, so a 403 here means the allowlist and nothing
else.
curl -i -X OPTIONS https://api.01.software/api/products \
-H "X-Publishable-Key: pk01_..." \
-H "Origin: https://your-app.example.com" \
-H "Access-Control-Request-Method: GET"| Response | Meaning |
|---|---|
204 with Access-Control-Allow-Origin | The origin is allowed |
403 with body "reason":"origin_not_allowed" | This step is the cause |
200 with Access-Control-Allow-Origin: * | The key header did not arrive, so nothing was tested — check the command |
503 | Stopped at the key/tenant lookup, before the CORS check — inconclusive |
Register customer OAuth origins and redirect URIs — only if you enabled customer accounts
This step applies only to hosted customer OAuth — today, the ecommerce
template with customer accounts enabled. The other templates sign customers in
with local email and password and need nothing registered. Check whether your
app has app/api/auth/customer/callback; if it does not, skip this step.
For hosted OAuth, one customer-oauth-clients row owns three separate lists:
allowedOriginsrequires at least one exact normalized browser Origin, such ashttps://your-app.example.com. The token endpoint checks it for browser-originated authorization-code exchanges and refresh requests when anOriginheader is present. A missingOriginis allowed for non-browser or server requests.allowedRedirectUrisrequires at least one exact callback URI.allowedLogoutUriscontains every exact post-logout URI the app uses.
The authorize page checks redirect URI and scope, not allowedOrigins. At each
field's actual runtime stage, a supplied value is matched against its own list
by exact string. There is no wildcard form, so an unregistered value is rejected
outright:
redirect_uri is not registered for this clientRegister the deployed browser Origin, callback URI, and any post-logout URIs in their matching fields. Your template's README lists the exact paths it uses.
allowedOrigins: https://your-app.example.com
allowedRedirectUris: https://your-app.example.com/api/auth/customer/callback
allowedLogoutUris: https://your-app.example.com/account
allowedLogoutUris: https://your-app.example.com/Preview deployments get their own generated URL. Exact matching means every preview Origin, callback URI, and post-logout URI needs its own entry. Unlike the workspace Browser API origins allowlist in step 2, no wildcard pattern covers an OAuth client entry.
These registries are separate. The workspace Browser API origins in step 2
gate API CORS. The OAuth client's allowedOrigins gates browser-originated token
and refresh requests when they carry an Origin header; it does not gate the
authorize page, and origin-less non-browser or server requests are allowed. Its
allowedRedirectUris and allowedLogoutUris gate the callback and post-logout
destinations. Satisfying one does nothing for the others — skipping an OAuth
client entry fails the applicable token, sign-in, or logout stage, not the
workspace API CORS check.
Deploy
At this point nothing is 01.software-specific. Follow your Next.js host's deployment documentation. For example, on Vercel, import the repository, configure only the required public and server-only values from step 1 in the host settings, and deploy. Vercel is not required or preferred. This is the one deployment action for initial production provisioning. Do not paste a local environment file. Any host that runs the Next.js app can own this step — the only 01.software-specific parts are the requirements above.
After that initial deployment, verify that the workload's server-side SDK or CLI calls work with the initial production key. This verifies credential reachability only; it does not prove deployment readiness. Existing-workload rotation follows its separate replacement, deploy/restart, verification, and revocation flow in step 1.
Verify the steps separately. The scaffolded templates call the platform from the server only — no client component in any template imports the SDK, so every call runs in a server component, route handler, or server action. A deployed page that looks healthy therefore proves step 1 and says nothing about step 2: it renders identically with an empty allowlist.
| What to confirm | How |
|---|---|
| Key placement (1) | The deployed page renders data |
No sk01_ leaked (1) | Sweep the build output — see below |
| Origin allowed (2) | Run the curl probe above against the deployed origin and read the response table |
A page that renders is not enough for step 1. If you gave a secret key a
NEXT_PUBLIC_ prefix to make something work, the page renders exactly the same
while the key ships to every visitor. Check the build output rather than the
deployed site — a leaked key usually lands in one chunk out of many, and that
chunk may not be loaded on the page you happen to be looking at:
pnpm build && grep -rl 'sk01_' .next/static .next/server/appThe && is load-bearing. A failed build still leaves .next/static and
.next/server/app behind from an earlier run, so a directory check would pass
while the search read stale output. Chaining on the build means the search runs
only against what this build produced.
No output is the acceptable answer. A hit means the key is already public and must be rotated, not just removed from the bundle.
Build with the same values the host uses. A variable defined only in your host's
dashboard is absent from a local build, so a leak that depends on it will not
appear here — and some, like COMMERCE_MODE, stop the build outright when
missing, which the && turns into a visible failure rather than a silent pass. This finds the sk01_ secret key; other secrets have no shared
prefix to search for.
Once you add your own browser-side call, loading that page confirms step 2 too.