Run a blueprint over the REST API
The five blueprint routes, who may call each one, the request and response shapes, and a curl example from plan to apply.
An API (application programming interface) is a way for one program to ask another to do something. The blueprint API is a set of REST routes: web addresses your program sends a request to, and gets JSON (JavaScript Object Notation) back from. The console and the command line use these same routes, so they give the same plan for the same file.
curl -X POST https://api.console.buildbase.app/api/blueprint/plan \
-H "Authorization: Bearer $BUILDBASE_API_TOKEN" \
-H "Content-Type: application/json" \
--data @request.jsonBefore you start
An admin key: an API key with the owner or admin role, issued as an admin
key. Before you
start shows how to make one. Send it
in the Authorization header, as for the Admin
API.
The server is https://api.console.buildbase.app on BuildBase's cloud, or your own server when self-hosted.
Routes
| Method | Path | Who may call it | What it does |
|---|---|---|---|
GET | /api/blueprint/schema | A role that may read organization settings | Returns the JSON Schema: every field a blueprint can hold |
POST | /api/blueprint/validate | A role that may create organization settings | Checks the file on its own. Changes nothing |
POST | /api/blueprint/plan | The owner or an admin | Compares the file with the organization. Changes nothing |
POST | /api/blueprint/apply | The owner or an admin | Makes the changes, then plans again to check |
GET | /api/blueprint/export | The owner or an admin | Returns the organization's current setup as a blueprint |
Plan, apply and export need the owner or admin role, because a blueprint covers the whole console. A custom role is not enough, even one that can see every screen. The owner and admins can call every route.
A request with a missing or wrong key, or a key without the role a route needs, gets 401. An app-server key gets 403 ADMIN_SCOPE_REQUIRED on every route here, whatever its role, and so does an app user's credential (Error codes). A read-only support session (impersonation) can validate and plan, but apply answers 403.
The request body
validate, plan and apply take the same JSON body. Send source or blueprint, not both.
| Field | Type | What it is |
|---|---|---|
source | string | The file as text. Prefer it: from text, BuildBase can report a key written twice, which a parsed object has already lost. Up to 4 MB |
blueprint | object | The same document, already parsed |
env | object | A value for each ${env:NAME} the file uses, such as { "APP_URL": "https://app.acme.example" }. Up to 200 names. Never stored or sent back |
prune | boolean | plan and apply: also delete what the file no longer lists, for the kinds in policy.prune. Off when left out |
planHash | string | apply only, and required: the hash of the plan you reviewed. A request without it, or with an empty one, is refused with 400 |
Any other field is refused with 400.
Why apply takes the plan hash
The hash is a short code that stands for the plan: which resources would change and how. Apply needs the planHash of the plan you reviewed. BuildBase makes the plan again and compares the two. If they differ, because someone changed the organization or you changed the file, apply refuses and nothing changes. So the changes you reviewed are the only changes that run. A request without a planHash never reaches the plan: it is refused with 400.
The responses
Every route answers 200 with the result, even when the file has problems. Read ok to know whether it worked.
validate
{
"ok": false,
"diagnostics": [
{
"level": "error",
"path": "/auth/clients/web/redirectUrls/0",
"message": "APP_URL has no value. Set the environment variable APP_URL (in the console, fill in its field), then try again."
}
],
"env": [{ "name": "APP_URL", "secret": false, "provided": false }]
}diagnosticslists each problem.leveliserrororwarning.pathpoints at the place in the file:/auth/clients/web/redirectUrls/0is the first entry ofredirectUrlsin the auth clientweb.envlists each${env:NAME}the file uses, whether it is in a secret field, and whether you sent a value.
plan
{
"ok": true,
"diagnostics": [],
"actions": [
{
"kind": "app",
"key": "",
"address": "app",
"type": "update",
"title": "App settings",
"id": "665f1a2b3c4d5e6f7a8b9c0d",
"changes": [{ "path": "name", "from": "My app", "to": "Acme" }]
},
{
"kind": "webhooks",
"key": "crm",
"address": "webhooks.crm",
"type": "create",
"title": "Webhook \"CRM sync\""
}
],
"summary": {
"create": 1,
"update": 1,
"version": 0,
"noop": 0,
"manual": 0,
"blocked": 0,
"conflict": 0,
"unknown": 0,
"delete": 0,
"unmanaged": 0
},
"hash": "3f2a9c1d0b7e4a65",
"changes": true,
"text": " ~ update App settings [app]\n name: \"My app\" -> \"Acme\"\n + create Webhook \"CRM sync\" [webhooks.crm]\n\nPlan: 1 to create, 1 to update, 0 new versions, 0 to delete, 0 unchanged."
}actionshas one entry for each resource the file names, and for each resource of those kinds that the file leaves out.typeis one of the plan actions.changeson an action lists each field that differs, old value first. A secret shows"secret": trueand never its value. A deploy hook URL or a workflow request's URL or header value shows"(secret)"in its place, and a change only there reads as a secret one.noteson an action, when present, says what to do about it.moveson an action, when present, is a publish withversionMoveson:holdersis how many a move would reach (nullwhen they could not be counted), withmode,noticeDaysand, when known, the consoleurlwhere the move waits for confirmation. It is not part of thehash: holders come and go, and apply never moves anyone itself. See version moves.changesat the top level istruewhen apply would change something.textis the plan as the command line prints it.
When ok is false, the file has errors, diagnostics lists them, and nothing was compared.
apply
{
"ok": true,
"plan": { "ok": true, "hash": "3f2a9c1d0b7e4a65", "changes": true },
"steps": [
{
"address": "app",
"type": "update",
"title": "App settings",
"status": "done",
"id": "665f1a2b3c4d5e6f7a8b9c0d"
},
{
"address": "webhooks.crm",
"type": "create",
"title": "Webhook \"CRM sync\"",
"status": "done",
"id": "6660aa1b2c3d4e5f6a7b8c9d",
"outputs": { "signingSecret": "the value, shown only here" }
}
],
"recheck": { "ok": true, "changes": false },
"text": "..."
}This example shortens plan and recheck, which are whole plans.
stepshas one entry for each change, in the order it ran.statusisdone,failedorskipped. A failed step carrieserror, andhttpStatuswhen a request was refused.- Apply stops at the first failed step. The steps after it are
skipped, and the changes already made stay. outputsholds the values shown only once, such as a webhook signing secret. This response is the only place they appear. Store them before you do anything else.moveon a step says what a publish did about its move:outcomeisawaiting_confirmation(it waits for an admin in the console, aturl, forholdersholders) ornone(no move was made). A blueprint never starts, confirms or cancels a move.recheckis the plan made again after apply."changes": falsemeans the organization now matches the file.
When apply refuses before any step runs, ok is false, steps is empty, there is no recheck, and plan.diagnostics says why. That happens when the file has errors, or when planHash does not match.
Only one apply runs for each organization at a time. A second one answers 409 with a message while the first runs. Each apply that changes something, or fails, writes a Blueprint applied row to the audit log, with the plan hash, how many steps ran and which failed.
export
{
"blueprint": {
"$schema": "https://docs.buildbase.app/schemas/blueprint.v1.json",
"version": 1,
"billing": {
"stripe": {
"publishableKey": "pk_test_51Hxxxxxxxxxxxxxxxx",
"secretKey": "${env:BILLING_STRIPE_SECRETKEY}"
}
}
},
"env": [{ "name": "BILLING_STRIPE_SECRETKEY", "secret": true }],
"skipped": []
}Secrets are never exported. Each one is a ${env:NAME} named after where it sits, and env lists the names to set before you plan the file. That includes values the console shows but that work as credentials: a deploy hook URL, and the URL and header values of a workflow's HTTP request. Set those to their current values to plan the file unchanged. A part of the console the key cannot read is left out.
Coupons are read from your Stripe account. When Stripe refuses the saved secret key, or does not answer, the export leaves coupons out and names them in skipped, each with its path (billing.coupons) and the server's reason; the rest of the organization still exports. A section listed there is not empty: fix the key in Billing > Credentials, then export again.
schema
The JSON Schema, the same document served at https://docs.buildbase.app/schemas/blueprint.v1.json.
From plan to apply with curl
curl sends web requests from a terminal, and jq builds and reads JSON. Both come with most Mac and Linux systems, or install jq first.
-
Set your key without showing it, as on the command line page:
read -rs BUILDBASE_API_TOKEN && export BUILDBASE_API_TOKEN -
Build the request from your file and the values it needs:
jq -n --rawfile source buildbase.blueprint.json \ --arg key "$STRIPE_SECRET_KEY" \ '{source: $source, env: {STRIPE_SECRET_KEY: $key}}' > request.json -
Plan, print the plan, and keep its hash:
curl -sS -X POST https://api.console.buildbase.app/api/blueprint/plan \ -H "Authorization: Bearer $BUILDBASE_API_TOKEN" \ -H "Content-Type: application/json" \ --data @request.json > plan.json jq -r .text plan.json -
Read the plan. If you agree, apply that plan:
jq --arg hash "$(jq -r .hash plan.json)" '. + {planHash: $hash}' request.json > apply.json curl -sS -X POST https://api.console.buildbase.app/api/blueprint/apply \ -H "Authorization: Bearer $BUILDBASE_API_TOKEN" \ -H "Content-Type: application/json" \ --data @apply.json > result.json jq -r .text result.json -
Move the shown-once values from
result.jsonto your secret store, then deleterequest.json,apply.jsonandresult.json: they hold secrets.
Next steps
- File format - what each section holds, and every plan action
- Command line - the same routes, with less to write
- Troubleshooting - what each message means and what to do