Skip to content

Cloning a Template

Cloning has three moves: find a template, preflight it so you know what it needs, then clone and poll until it’s done.

  1. Go to dashboard.butterbase.ai and open Templates.
  2. Search by name, or sort by Recent / Popular (popularity is the clone count).
  3. Each card shows the app name, its owner, and its region. The card’s only action is Clone — the dashboard has no template detail view, so use GET /v1/templates/{app_id} (or find_templates) if you want the table and function inventory before committing.
Terminal window
butterbase templates
butterbase templates --q blog --sort popular --limit 20
butterbase templates --region us-west-2 --json
manage_app action: "find_templates"
q: "blog"
sort: "popular" # "recent" (default) or "popular"
limit: 10
offset: 0

Returns { items: [...], total, limit, offset }.

GET /v1/templates?q=blog&sort=popular&limit=20&offset=0
GET /v1/templates/{app_id}

Discovery is anonymous — no credentials needed to browse. The detail endpoint returns the template’s table and function inventory, with platform-internal tables filtered out.

Before you clone, ask the template what it will need from you. This costs nothing and requires no auth for public apps.

GET /v1/templates/{source_app_id}/clone-preflight
manage_app action: "preview_clone_env_vars"
source_app_id: "<app_id>"
{
"functions": [
{
"fn_name": "send-invite",
"keys": ["BUTTERBASE_API_URL", "BUTTERBASE_API_KEY", "RESEND_API_KEY"],
"key_meta": [
{ "key": "BUTTERBASE_API_URL", "status": "auto_filled", "reason": "Platform-resolved at clone time." },
{ "key": "BUTTERBASE_API_KEY", "status": "auto_filled", "reason": "Auto-minted bb_sk_* scoped to the new app." },
{ "key": "RESEND_API_KEY", "status": "user_required" }
]
}
],
"durable_objects": { "env_keys": ["OPENAI_API_KEY"], "key_meta": [ ] },
"app_env": { "keys": ["FEATURE_FLAGS"], "note": "These app-level env vars are copied to the clone." }
}

Only key names are ever returned. Values never leave the source app.

statusWhat it means for you
auto_filledButterbase supplies it. BUTTERBASE_API_URL and BUTTERBASE_APP_ID are resolved to your new app; BUTTERBASE_API_KEY is minted as a fresh bb_sk_* scoped to your clone; a meetings webhook secret is minted and wired in if the source had one.
user_requiredA third-party secret you must provide — at clone time via env_var_values, or afterwards.

durable_objects.env_keys are never carried across, convention keys aside. Plan to re-set them after the clone with manage_durable_objects action: "set_env".

Click Clone on the template. The modal asks for a name, runs the preflight, and shows an env-vars step where you can fill in each user_required key before starting. Progress is shown live; you land on the new app’s overview when it completes.

A clone in progress, showing the replaying_rls stage

Terminal window
butterbase clone <source_app_id> ./my-clone --name "My App" --region us-west-2

This creates the app, pulls the source’s latest repo snapshot into ./my-clone/, and rewrites .butterbase/config.json with the new app id. Both --name and --region are optional; omit the region and the clone inherits the source’s.

manage_app action: "clone"
source_app_id: "<app_id>"
name: "My App"
region: "us-west-2"
env_var_values: { "send-invite": { "RESEND_API_KEY": "re_..." } }
auto_mint_api_key: [ { "fn_name": "send-invite", "key": "BUTTERBASE_API_KEY" } ]

Returns { job_id, status, pending_env_vars }. pending_env_vars is the per-function map of keys still needing values — an empty object means you’re fully configured.

POST /v1/templates/{source_app_id}/clone
Authorization: Bearer {token}
{
"name": "My App",
"region": "us-west-2",
"organization_id": "<org-uuid>",
"env_var_values": { "send-invite": { "RESEND_API_KEY": "re_..." } },
"auto_mint_api_key": [ { "fn_name": "send-invite", "key": "BUTTERBASE_API_KEY" } ]
}
FieldNotes
nameOptional. Globally unique across Butterbase — a collision returns 409. Omit it and you get Clone of {source}.
dest_regionOptional (region is accepted as a legacy alias). Defaults to the source’s region. If the region you ask for is temporarily closed to new apps, the clone is redirected to an open one rather than failing. See Regions.
organization_idOptional. Must be an org you belong to. Defaults to the org bound to your credentials, then your personal org.
env_var_values{ fn_name: { KEY: "value" } }. Values are stored encrypted against the new app.
auto_mint_api_key[{ fn_name, key }] — mint a scoped bb_sk_* for that key instead of supplying one.
GET /v1/clone-jobs/{job_id}
manage_app action: "get_clone_job", job_id: "<job_id>"
{
"job_id": "",
"status": "replaying_functions",
"source_app_id": "app_src",
"dest_app_id": null,
"retry_count": 0,
"error_message": null,
"warnings": [],
"unfilled_env_vars": { "send-invite": ["RESEND_API_KEY"] },
"created_at": "",
"completed_at": null
}

status walks through pendingprocessingreplaying_schemareplaying_rlsreplaying_durable_objectsreplaying_functionsreplaying_configcopying_reposeeding_datacompleted.

dest_app_id is populated once the app exists. Only the requester can read their own job.

If it fails:

POST /v1/clone-jobs/{job_id}/retry

Read error_message first — a retry re-runs the same pipeline, so a genuine configuration problem will fail the same way twice.

A completed job can still carry warnings[]. These are soft failures the pipeline worked around rather than aborted on. Common ones:

  • The source’s auth_hook_function pointed at a function that couldn’t be replayed — the binding is left NULL.
  • A config value referenced a secret that belongs to the source owner and wasn’t copied.
  • A meetings webhook secret was minted — the warning contains the wsec_* once, so capture it.

The dashboard does not show warnings. Its clone flow drops you on the new app’s overview with no warnings panel, even with ?cloneJob= in the URL — so a dashboard-only clone can silently lose a one-time secret. Read them over the API or MCP instead:

manage_app action: "get_clone_job", job_id: "<job_id>"
GET /v1/clone-jobs/{job_id}

Do this after every dashboard clone, not just failed ones.

CodeHTTPMeaningFix
RESOURCE_NOT_FOUND404Source doesn’t exist or isn’t publicCheck the app id; ask the owner to make it public
VALIDATION_INVALID_SCHEMA400Source has no repo snapshotThe owner must run butterbase repo push at least once
VALIDATION_INVALID_SCHEMA409App name already takenPick a different name
CLONE_LIMIT_INFLIGHT4293 clones already in progressWait for one to finish
rate limited429More than 5 clones started this hourWait
project limit403Destination org is at its app quotaDelete an app or upgrade — see Plans & Usage

Your clone is running but it isn’t finished. Work through Configuring Your Clone.