BengarTrust infrastructure

Docs · Reference

API reference

83 routes, extracted from the Gateway’s own source. The ones below carry request and response shapes; the rest are listed in full at the end.

Base URL is your deployment’s Gateway. There is no hosted Bengar API to point at yet, and this page does not pretend otherwise.

Machine routes authenticate with authorization: Bearer bk_…. Console routes authenticate with a session cookie and require a matching CSRF token on every mutation.

Authorization

One decision from several inputs: what the permit allows, what the policy allows, and whether a person has to be asked.

POST/v1/authorize

Decide whether an agent may act, and optionally execute what was allowed.

Auth: API key

Request

{
  "agent":    "did:key:z6Mk…",          // required
  "permit":   "<permit artifact>",       // required
  "action":   "purchase",                // required, ≤ 64 chars
  "resource": "resource://company/procurement/laptop",  // required, ≤ 512
  "amount":   { "minor": 500000, "currency": "TRY" },   // optional
  "intent":   "<intent id>",             // optional

  // At most ONE of these. Both together is refused: an execution calls a
  // merchant and a chain authorization issues an artifact, and doing both
  // leaves nobody able to say what the ALLOW permitted.
  "execution": {
    "merchant":  "acme-procurement",
    "operation": "create_purchase_order",
    "payload":   { },
    "nonce":     "<caller-chosen, unique per action>"
  }
}

Response

{
  "decision":       "ALLOW" | "DENY" | "REQUIRE_APPROVAL" | "REPLAY",
  "reason_code":    "policy_allow",
  "policy_version": 3,
  "request_id":     "…",
  "audit_id":       "…",

  // Present only when the decision was ALLOW and an execution was requested.
  // Its absence is the statement that nothing was called — there is no empty
  // object and no null status a caller could read as a partial success.
  "execution": { "id": "…", "status": "SUCCEEDED" | "FAILED" | "UNKNOWN" }
}

Errors

CodeMeaning
REQUIRE_APPROVALNot an allow. A human must decide, and the action must not proceed. It is its own string rather than an ALLOW carrying a flag, so treating it as “go” has to be a deliberate mistake.
permit_invalidThe permit does not verify, or does not cover this.
agent_frozenThe agent is stopped. Nothing it presents will authorize.
execution_in_progressA concurrent execution of this action is in flight.
unauthorizedThe API key is missing, wrong or revoked.

Idempotency

`execution.nonce` binds one execution to one action. Repeating the same request with the same nonce returns the stored outcome as `decision: “REPLAY”` — no merchant call, no budget spent, no approval consumed, and no new decision.

There is no `url` field in `execution`, and sending one is refused by name. The merchant is named, and where that name points is the deployment's configuration rather than the caller's.

Example

curl -sX POST https://gateway.example.com/v1/authorize \
  -H "authorization: Bearer $BENGAR_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "agent": "did:key:z6MkProcurement",
    "permit": "'"$PERMIT"'",
    "action": "purchase",
    "resource": "resource://company/procurement/laptop",
    "amount": { "minor": 500000, "currency": "TRY" }
  }'
import { Bengar } from "@bengar/sdk";

const bengar = new Bengar({ apiKey: process.env.BENGAR_API_KEY! });

const decision = await bengar.authorize({
  agent: "did:key:z6MkProcurement",
  permit,
  action: "purchase",
  resource: "resource://company/procurement/laptop",
  amount: { minor: 500_000, currency: "TRY" },
});

// Three values, and only one of them means go.
if (decision.decision !== "ALLOW") {
  // REQUIRE_APPROVAL is not an allow. Wait for the human.
  return;
}

Execution

What happened when an ALLOW was acted on, and how an outcome nobody observed is settled.

GET/v1/projects/:projectId/executions

List executions, newest first.

Auth: API key

Response

{ "executions": [ { "id", "status", "agent", "operation", "created_at" } ] }
POST/v1/projects/:projectId/executions/resolve

Record what a person established about an UNKNOWN execution.

Auth: API key

Request

{ "execution_id": "…", "outcome": "SUCCEEDED" | "FAILED", "note": "what was checked" }

Errors

CodeMeaning
execution_not_unknownThat execution already has an outcome.
conflictSomebody else resolved it first. Their statement stands.

UNKNOWN is not FAILED. It means the action may or may not have happened, and it is settled by somebody finding out — never by retrying.

Policy

Deterministic rules, versioned, simulated before they decide anything.

GET/v1/projects/:projectId/policy

The active version and its document hash.

Auth: API key

GET/v1/projects/:projectId/policy/versions

Every version, with its state and who created it.

Auth: API key

PUT/v1/projects/:projectId/policy

Save a new version. Saving does not activate it.

Auth: API key · Roles: POLICY_AUTHOR

Request

{ "policy": { "spec_version": "…", "rules": [ … ] }, "note": "why" }

Errors

CodeMeaning
invalid_requestThe document did not validate. Every error is returned at once, located, so they can be fixed in one pass.
POST/v1/projects/:projectId/policy/simulate

Ask what the policy would decide, without deciding anything.

Auth: API key · Roles: POLICY_AUTHOR, ADMIN, AUDITOR

Request

{
  "request": {
    "agent":    "did:key:z6Mk…",   // required — a simulation is about somebody
    "action":   "purchase",
    "resource": "resource://company/procurement/laptop",
    "amount":   { "minor": 500000, "currency": "TRY" }
  },
  "policy": { … }   // optional: simulate a candidate instead of the active one
}

Response

{ "policy_decision", "policy_reason", "policy_version", "matched_rules", "steps" }

A simulation authorizes nothing, spends no counter and writes no audit event. The final decision is the meet of this and the permit.

POST/v1/projects/:projectId/policy/activate

Make a saved version the one that decides.

Auth: API key · Roles: POLICY_AUTHOR

Request

{ "version": 4 }

ADMIN does not acquire this by being ADMIN. Activation is when a policy starts deciding, and it is audited whether it succeeds or is refused.

Approvals

The obligation a REQUIRE_APPROVAL opens, and how it is discharged.

GET/v1/projects/:projectId/approvals

Approval requests and their status.

Auth: API key

POST/v1/projects/:projectId/approvals

Record an approval that was made elsewhere.

Auth: API key

An approval satisfies the human-approval condition. It does not itself grant authority — the permit and the policy still decide.

POST/v1/projects/:projectId/approvals/revoke

Withdraw an approval that has not been consumed.

Auth: API key

Agents

Stopping an agent, and reading whether it is stopped.

GET/v1/projects/:projectId/agents/state

Whether an agent is frozen, and since when.

Auth: API key

POST/v1/projects/:projectId/agents/freeze

Stop an agent immediately.

Auth: API key

A freeze takes effect on the next authorization, not on the next epoch. An agent frozen mid-flight does not get to finish.

POST/v1/projects/:projectId/agents/release

Let a frozen agent act again.

Auth: API key

Proofs

Checking an audit event against what was anchored, and against a witness.

GET/v1/projects/:projectId/audit/:auditId/proof

The inclusion proof for one audit event.

Auth: API key

Response

{ "audit_event_id", "root", "leaf_version", "path": [ … ], "anchored_height" }
POST/v1/console/projects/:projectId/proofs/verify

Verify an event and ask an independent witness about the root.

Auth: Console session · Roles: ADMIN, AUDITOR

Response

{
  "verification": "VERIFIED" | "INVALID" | "NOT_WITNESSED" | "UNAVAILABLE",
  "independently_witnessed": true,
  "reason": null,
  "evidence": { "local_proof", "chain_witness", "root", "anchored_height", "finality" }
}

Four outcomes, and they are not three. NOT_WITNESSED means we looked and the witness does not hold this root; UNAVAILABLE means we could not look. Reporting either as INVALID is a false accusation.

Console control plane

Session-authenticated routes the Console uses. Every one re-authorizes against the role table; hiding a link is not a control.

POST/v1/console/login

Exchange a password for a session and a CSRF token.

Auth: None

Argon2id. The session cookie is HttpOnly; the CSRF token is a separate cookie the browser must echo on every mutation.

GET/v1/console/projects

Every project this person is a member of.

Auth: Console session

POST/v1/console/projects/:projectId/api-keys

Mint an API key. The secret is returned once and never again.

Auth: Console session · Roles: ADMIN

The hash is stored, the secret is not. A lost key is revoked and replaced rather than recovered.

POST/v1/console/projects/:projectId/approvals/:approvalRequestId/challenge

Open an approval ceremony: returns the WebAuthn challenge and the Gateway's own reading of the obligation.

Auth: Console session · Roles: APPROVER

The screen a person reads is rendered from this response, not from the page they were looking at. Display-one-sign-another is prevented by not having a second copy.

POST/v1/console/projects/:projectId/approvals/challenges/:challengeId/complete

Finish the ceremony: verify the assertion and have Custody sign the approval.

Auth: Console session · Roles: APPROVER

The passkey proves a person was present. The approval signature is Ed25519 from Custody over mTLS, and the browser never holds that key.

Asset Studio

Issuing and moving an organization's own assets. Mounted only where a deployment signs for a chain; absent, not empty, where it does not.

GET/v1/console/projects/:projectId/chain-key

The organization's chain signing address, or its absence.

Auth: Console session · Roles: ADMIN, AUDITOR

An address and a public key. There is no route that returns key material, because Custody has no method that could produce it.

POST/v1/console/projects/:projectId/chain-key

Provision the organization's signing key inside Bengar Custody.

Auth: Console session · Roles: ADMIN

A separate administrative act from holding ADMIN, with its own audit line. An administrator without it can read assets and cannot move them (ADR-084).

GET/v1/console/projects/:projectId/assets

What this organization has issued, from the read model.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR

Derived figures, each carrying the height they were derived at. A supply with no height is a number nobody can act on.

GET/v1/console/projects/:projectId/assets/:assetId

One asset: supply, holders, transfers, supply history and gating.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR

POST/v1/console/projects/:projectId/asset-intents

Build one immutable intent and return the review projected from it. Nothing is signed and nothing reaches the chain.

Auth: Console session · Roles: ADMIN

The intent is fingerprinted over every field the signature will commit to, including the organization — so two organizations issuing an identical transaction have different fingerprints.

GET/v1/console/projects/:projectId/asset-intents

What this organization authorized, and what the chain said.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR

POST/v1/console/projects/:projectId/asset-intents/:fingerprint/submit

Sign the stored intent in Custody and broadcast it.

Auth: Console session · Roles: ADMIN

The request body is ignored entirely — the fingerprint in the path selects what gets signed. An intent that already has an answer returns it rather than being signed again.

POST/v1/console/projects/:projectId/asset-intents/:fingerprint/resolve

Record what a person established about an UNKNOWN outcome.

Auth: Console session · Roles: ADMIN

There is no retry endpoint and there will not be one. The way to settle "we do not know whether this happened" is to find out.

Organization treasury

An address the organization controls, and moving the balance in it. Holding the key is not permission: every transfer is decided by the organization's own policy first.

GET/v1/console/projects/:projectId/treasury

The organization's own address, its balance and its recent native transfers.

Auth: Console session · Roles: ADMIN, AUDITOR

The organization's own account, not the network's community pool. No key is provisioned means no address at all, which is a different answer from a balance of zero.

POST/v1/console/projects/:projectId/treasury/intents

Build one immutable transfer intent and return the authorization decision. Nothing is signed.

Auth: Console session · Roles: ADMIN

The decision names every conjunct, including the permit — which does not apply, and says so rather than reporting a check that did not run. Reviewing never discharges an approval: a stored approval is single-use.

POST/v1/console/projects/:projectId/treasury/intents/:fingerprint/submit

Decide again, sign in Custody, and broadcast.

Auth: Console session · Roles: ADMIN

The request body is ignored entirely. The decision is re-made here and it governs; a review is a projection, never a permission that was banked. An intent that already has an answer returns it rather than being signed again.

Network views

Public chain facts, read through the Gateway on a person's behalf. Read-only: there is no write route in this family, and none is planned here.

GET/v1/console/projects/:projectId/network

Chain identity, indexed height, chain head, lag and the read model's sync state.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR, PERMIT_ISSUER

Nothing here is tenant data — Scan serves the same facts to strangers — but a session and a membership are still required, because these are the Console's routes.

GET/v1/console/projects/:projectId/network/validators

Validators this read model holds, with delegations seen and jailed status.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR, PERMIT_ISSUER

Moniker and commission are absent because the read model does not hold them; both live in a message body and in no event.

GET/v1/console/projects/:projectId/network/validators/:address

One validator, with the delegations to it.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR, PERMIT_ISSUER

GET/v1/console/projects/:projectId/network/staking

Delegations by validator, beside native supply.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR, PERMIT_ISSUER

No total is returned: a slash moves tokens without moving shares, so a sum of delegations is not a sum of stake.

GET/v1/console/projects/:projectId/network/governance

Token-governance proposals, and the Bengar authority's configuration and members.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR, PERMIT_ISSUER

Both authorities, because either alone misleads: neither can act for the other and there is no universal admin.

GET/v1/console/projects/:projectId/network/governance/:proposalId

One proposal, with the votes cast, the deposits and what executed.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR, PERMIT_ISSUER

GET/v1/console/projects/:projectId/network/treasury

The network treasury's completed payments, each with the proposal that authorised it.

Auth: Console session · Roles: ADMIN, AUDITOR, APPROVER, POLICY_AUTHOR, PERMIT_ISSUER

The network's community pool, not an organization's treasury. Funding is deliberately inactive and spending needs a passed proposal and an N-of-M consent together.

Every registered route

Extracted from the Gateway’s source. The 44 not detailed above are control-plane routes the Console uses; they are listed so that nothing is hidden, not because an integrator needs them.

MethodPath
GET/v1/console/projects
GET/v1/console/projects/:projectId/agents
GET/v1/console/projects/:projectId/agents/:agentDid
GET/v1/console/projects/:projectId/api-keys
GET/v1/console/projects/:projectId/approvals
GET/v1/console/projects/:projectId/approvals/:approvalId
GET/v1/console/projects/:projectId/asset-intents
GET/v1/console/projects/:projectId/assets
GET/v1/console/projects/:projectId/assets/:assetId
GET/v1/console/projects/:projectId/audit
GET/v1/console/projects/:projectId/chain-key
GET/v1/console/projects/:projectId/control-plane
GET/v1/console/projects/:projectId/dashboard
GET/v1/console/projects/:projectId/executions
GET/v1/console/projects/:projectId/me
GET/v1/console/projects/:projectId/members
GET/v1/console/projects/:projectId/memberships
GET/v1/console/projects/:projectId/network
GET/v1/console/projects/:projectId/network/governance
GET/v1/console/projects/:projectId/network/governance/:proposalId
GET/v1/console/projects/:projectId/network/staking
GET/v1/console/projects/:projectId/network/treasury
GET/v1/console/projects/:projectId/network/validators
GET/v1/console/projects/:projectId/network/validators/:address
GET/v1/console/projects/:projectId/permits
GET/v1/console/projects/:projectId/permits/:commitment
GET/v1/console/projects/:projectId/policy
GET/v1/console/projects/:projectId/policy/versions
GET/v1/console/projects/:projectId/principals/:principalId/approval-keys
GET/v1/console/projects/:projectId/settings
GET/v1/console/projects/:projectId/treasury
GET/v1/console/webauthn/credentials
GET/v1/projects/:projectId/agents/state
GET/v1/projects/:projectId/approvals
GET/v1/projects/:projectId/audit/:auditId/proof
GET/v1/projects/:projectId/executions
GET/v1/projects/:projectId/policy
GET/v1/projects/:projectId/policy/versions
GET/v1/projects/:projectId/sod
POST/v1/authorize
POST/v1/console/login
POST/v1/console/logout
POST/v1/console/projects/:projectId/agents
POST/v1/console/projects/:projectId/agents/freeze
POST/v1/console/projects/:projectId/agents/release
POST/v1/console/projects/:projectId/api-keys
POST/v1/console/projects/:projectId/api-keys/:keyId/revoke
POST/v1/console/projects/:projectId/approval-keys/:keyId/retire
POST/v1/console/projects/:projectId/approvals/:approvalRequestId/challenge
POST/v1/console/projects/:projectId/approvals/challenges/:challengeId/complete
POST/v1/console/projects/:projectId/asset-intents
POST/v1/console/projects/:projectId/asset-intents/:fingerprint/resolve
POST/v1/console/projects/:projectId/asset-intents/:fingerprint/submit
POST/v1/console/projects/:projectId/chain-key
POST/v1/console/projects/:projectId/executions/:executionId/resolve
POST/v1/console/projects/:projectId/members
POST/v1/console/projects/:projectId/members/:principalId/revoke
POST/v1/console/projects/:projectId/members/:principalId/roles
POST/v1/console/projects/:projectId/members/:principalId/roles/:role/revoke
POST/v1/console/projects/:projectId/permits
POST/v1/console/projects/:projectId/permits/:commitment/revoke
POST/v1/console/projects/:projectId/policy/activate
POST/v1/console/projects/:projectId/policy/simulate
POST/v1/console/projects/:projectId/principals/:principalId/approval-keys
POST/v1/console/projects/:projectId/proofs/verify
POST/v1/console/projects/:projectId/treasury/intents
POST/v1/console/projects/:projectId/treasury/intents/:fingerprint/submit
POST/v1/console/register
POST/v1/console/webauthn/credentials/:id/disable
POST/v1/console/webauthn/registration
POST/v1/console/webauthn/registration/options
POST/v1/organizations/policy-signing-key
POST/v1/projects/:projectId/agents/freeze
POST/v1/projects/:projectId/agents/release
POST/v1/projects/:projectId/approvals
POST/v1/projects/:projectId/approvals/revoke
POST/v1/projects/:projectId/executions/resolve
POST/v1/projects/:projectId/policy/activate
POST/v1/projects/:projectId/policy/simulate
POST/v1/projects/:projectId/principals
PUT/v1/console/projects/:projectId/policy
PUT/v1/projects/:projectId/policy
PUT/v1/projects/:projectId/sod

Errors

Every code, whether it may be retried, and what to do about it: the error reference.