BLOMBoundary Layer Labs
All guides

DocsDevelopers

The public API

Call BLOM over HTTPS from a script, CI, or your own tools. Each operation is one POST, checked as the BLOM app's calls are, under a published contract.

In BLOMOrganizationConnect Agent

Addresses

The base address is https://blom.boundarylayerlabs.com/api/v1. Under The public API, Connect Agent shows it with your organization's own path.

RequestWhat it does
POST /api/v1/o/{organizationId}/op/{operation}Calls one operation, such as tasks.capture, in your organization.
GET /api/v1/meSays who the token is: a person or an agent, its organization and role, an agent's accountable owner, and the operations it may call now. It grants nothing.
GET /api/v1/openapi.jsonThe contract. It needs no token.

Authentication

Every call carries Authorization: Bearer <token>. BLOM takes two kinds of token, and tells them apart by how they start:

An agent's key, blom_agent_…
A registered agent's own key, made on API keys. Calls act as the agent and are recorded with the person it answers to. Use it in scripts and CI.
A CLI sign-in, blom_cli_at_…
The access token of a person's sign-in through the CLI. Calls act as that person. The CLI gets it through the browser and refreshes it, since it lasts an hour.

BLOM has no personal API tokens: a person reaches the API through the CLI. The API reads no cookie, so a session in the BLOM app can't call it, and its answers carry no CORS headers. A token works only in its own organization: a path that names another answers 404.

What each may call

A person may call what they may do in the BLOM app, except what stays there: agents' keys, roles, and delegations; registering and deactivating agents; invitations; repository connections; creating an organization; and Design Lab levels. Reading and changing settings is open to people here too, under the same rules as in the BLOM app.

An agent's key reaches the same operations, less what stays with people: corrections, gap records, token use, AI contexts, and accepting or discarding a proposal. Its access, Read only, Read and write, or Admin, and where it reaches narrow it further. An operation outside the caller's set answers 404 with the reason not_offered, and one inside it still checks the caller's access.

When a token is refused

BLOM answers 401 with WWW-Authenticate: Bearer realm="blom" and, unless the token was missing, error="invalid_token" and the reason, such as token_expired or credential_revoked. An agent's key whose access reaches nothing answers 403 insufficient_scope. The answer's recovery says what to do.

The contract

/api/v1/openapi.json is an OpenAPI 3.1.0 document that BLOM builds from its own operations, so it lists exactly what the API offers. Each operation's path gives its input and its output as JSON Schema, with three marks of BLOM's own:

  • x-blom-kind: read or write.
  • x-blom-access: the role it needs, viewer, editor, or administrator.
  • x-blom-agents: whether an agent's key may call it.

The contract's version is v1, in its info and in every address. info.x-blom-contract-sha256 holds the document's own SHA-256, so you can tell when it has changed. Read answers by field name and ignore fields you don't know, as BLOM's CLI does.

Requests and answers

Send each call as a POST with Content-Type: application/json. The body holds the operation's input, and a write also holds a requestId, a new UUID. Nothing else may be in it:

{"input": {"page": {"limit": 10}}}
    {"input": {"taskId": "…", "body": "Expiry check passes."}, "requestId": "…"}

A read answers 200 with {"data": …}. A write answers 200 with {"data": …, "receipt": …}, and the receipt records the change: receiptId, requestId, committedAt, actorId, accountableOwnerId (null for a person), channel, changeId, and replayed. A call through BLOM's CLI is recorded with the channel cli, and any other with api.

Retrying a write

Use a new requestId for each change you mean to make, and send the same one, with the same input, when you retry. For 7 days a repeat changes nothing again: BLOM answers the first result with "replayed": true, or answers 409 already_applied with the first receipt's facts. A repeat can also answer 409 with one of these reasons:

  • in_progress: the first call is still running. Send it again shortly.
  • idempotency_conflict: the id was used with other input. Use a new id.
  • idempotency_expired: the id is more than 7 days old. Use a new id.

A Design Lab upload is the one call that isn't a POST: designLab.prepareUpload answers an uploadPath, and you PUT the file's bytes there. The CLI's design post does both. See Post a design.

Errors

A refusal is problem details (RFC 9457), sent as application/problem+json. It always holds type, title, status, code, and requestId, and adds what applies:

  • reason: a short code that says why, such as not_offered.
  • paths: the input fields that failed, never their values.
  • retrySafe, and retryAfterSeconds with a Retry-After header, when waiting helps.
  • recovery: the step to take, when a token is refused.
  • refusal: a refused claim's own words, and the claim and overlaps it names.

Every answer carries an x-request-id header, the same id as a problem's requestId. Quote it when you write to us.

CodeStatusWhat it means
invalid_input400The input doesn't match the contract, and paths names the fields. A body over 1 MiB answers 413, one that isn't JSON 415, and a method other than POST 405.
unauthenticated401No token, or one BLOM doesn't accept now.
forbidden403The caller may not do this.
not_found404Nothing with that id is there for the caller, or the operation is unknown or not offered.
conflict409The record changed since it was read, or a request id can't be used again.
gate_unmet409A gate of your PDLC isn't met.
precondition_failed409Something the operation needs isn't so, such as a webhook that is paused.
rate_limited429Too many calls this minute. Wait the seconds Retry-After names.
internal500BLOM failed. With retrySafe, send the same request again.
source_unavailable503A source BLOM depends on didn't answer.

Over the rate limit, for example:

HTTP/1.1 429 Too Many Requests
    Content-Type: application/problem+json
    Retry-After: 23

    {"type":"about:blank","title":"Rate limited","status":429,"code":"rate_limited","requestId":"…","reason":"rate_limited","retrySafe":true,"retryAfterSeconds":23}

Try it with curl

With an agent's key in BLOM_TOKEN, ask BLOM who the key is:

curl -s https://blom.boundarylayerlabs.com/api/v1/me \
      -H "Authorization: Bearer $BLOM_TOKEN"

The answer's data.organization.organizationId is the id the other paths need. Put it in ORG, then read the agent's queue, 10 entries at a time:

ORG="<organization id>"
    curl -s -X POST "https://blom.boundarylayerlabs.com/api/v1/o/$ORG/op/queue.readForActor" \
      -H "Authorization: Bearer $BLOM_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"input": {"page": {"limit": 10}}}'

Then add a note to a task. It's a write, so it carries a new request id, such as one uuidgen prints, and the key needs Read and write access:

curl -s -X POST "https://blom.boundarylayerlabs.com/api/v1/o/$ORG/op/tasks.addNote" \
      -H "Authorization: Bearer $BLOM_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"input": {"taskId": "<task id>", "body": "Expiry check passes."}, "requestId": "<new UUID>"}'

Calls count toward a rate limit of 120 a minute for each caller and 2,400 for the organization, shared with BLOM's MCP server. Limits lists the rest.

Can't find what you need?

Write to us, and we'll answer and add it here.

service@boundarylayerlabs.com