Skip to content
BuildBase

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.json

Before 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

MethodPathWho may call itWhat it does
GET/api/blueprint/schemaA role that may read organization settingsReturns the JSON Schema: every field a blueprint can hold
POST/api/blueprint/validateA role that may create organization settingsChecks the file on its own. Changes nothing
POST/api/blueprint/planThe owner or an adminCompares the file with the organization. Changes nothing
POST/api/blueprint/applyThe owner or an adminMakes the changes, then plans again to check
GET/api/blueprint/exportThe owner or an adminReturns 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.

FieldTypeWhat it is
sourcestringThe 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
blueprintobjectThe same document, already parsed
envobjectA 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
prunebooleanplan and apply: also delete what the file no longer lists, for the kinds in policy.prune. Off when left out
planHashstringapply 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 }]
}
  • diagnostics lists each problem. level is error or warning. path points at the place in the file: /auth/clients/web/redirectUrls/0 is the first entry of redirectUrls in the auth client web.
  • env lists 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."
}
  • actions has one entry for each resource the file names, and for each resource of those kinds that the file leaves out. type is one of the plan actions.
  • changes on an action lists each field that differs, old value first. A secret shows "secret": true and 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.
  • notes on an action, when present, says what to do about it.
  • moves on an action, when present, is a publish with versionMoves on: holders is how many a move would reach (null when they could not be counted), with mode, noticeDays and, when known, the console url where the move waits for confirmation. It is not part of the hash: holders come and go, and apply never moves anyone itself. See version moves.
  • changes at the top level is true when apply would change something.
  • text is 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.

  • steps has one entry for each change, in the order it ran. status is done, failed or skipped. A failed step carries error, and httpStatus when a request was refused.
  • Apply stops at the first failed step. The steps after it are skipped, and the changes already made stay.
  • outputs holds 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.
  • move on a step says what a publish did about its move: outcome is awaiting_confirmation (it waits for an admin in the console, at url, for holders holders) or none (no move was made). A blueprint never starts, confirms or cancels a move.
  • recheck is the plan made again after apply. "changes": false means 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.

  1. Set your key without showing it, as on the command line page:

    read -rs BUILDBASE_API_TOKEN && export BUILDBASE_API_TOKEN
  2. 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
  3. 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
  4. 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
  5. Move the shown-once values from result.json to your secret store, then delete request.json, apply.json and result.json: they hold secrets.

Next steps