Build an app with an agent
The goal: your own agent runtime (Claude Code, or anything that speaks MCP) reads the repository and database attached to one of your app groups, writes code, and opens a pull request — while Axowl decides what it is allowed to touch and records what it did.
Axowl does not build the application and does not host a screen for it. It supplies the materials and the authority.
Before you start
Section titled “Before you start”Two things are a person’s job, not the agent’s. Do them first:
- Connect a Git repository to the client the agent will work on.
- Attach a database to the app group.
The hire dialog checks both and will point you at the setup screens if either is missing. An agent only ever gets read on these — connecting them is a prerequisite, not something it can do.
1. Hire the agent
Section titled “1. Hire the agent”App group → Agents → Hire. Pick a kind, name it, give it a TTL (required — an agent without an expiry cannot be created), and choose the client it works on.
There is no scope picker on this screen. The kind decides the scopes. That is deliberate: the permissions live in the role, and letting the screen choose them too would create a second source of truth that drifts.
For the workflow in this guide you need three scopes:
| Need | Scope |
|---|---|
| Read the repository | org.git_repository.read |
| Open pull requests | org.git_repository.propose_change |
| Read the database | org.data_source.read |
design already holds all three, so it is the shortest path even though the name suggests
otherwise. Check the kind table in Agents before assuming.
You do not have to get this right up front. A kind that lacks a scope can ask for it through
approval once it is connected — see When something is refused below. Hiring test and letting it
request propose_change is a perfectly good way to see the whole loop.
If you want a different set
Section titled “If you want a different set”custom starts with nothing. Hiring it and connecting straight away gets you a refusal on every
gated tool. To give it authority, add permissions to the AGENT_CUSTOM role on the roles screen.
2. Connect your runtime
Section titled “2. Connect your runtime”claude mcp add --transport http axowl https://testapi.axowl.com/mcpThe first tool call returns 401, your client discovers the authorization server, and a browser opens. Sign in, pick the agent it may act as, approve. See MCP server for the alternative: a credential issued from the agent’s card, with no browser step.
3. Check what you actually have
Section titled “3. Check what you actually have”Call axowl_context first, always:
agent : App Builder (design)org : acmeapp group : 6f1c…client : Storefront (client_e7bce279e7974411a858)sign-in : https://login.axowl.com/login/client_e7bce279e7974411a858repository : acme/storefront (branch main)database : attached (production-read)
you may: read repository files yes read database yes open pull requests yesIt answers the two questions that otherwise cost you a wasted attempt: is anything connected, and
may I touch it. A NO line names the missing scope and says whether the agent or you is the one
lacking it.
Read the documentation, don’t infer it
Section titled “Read the documentation, don’t infer it”axowl_docs_list and axowl_docs_read serve this documentation site through the tool surface.
Point your runtime at them whenever the task touches Axowl itself — sign-in, permissions, seals,
billing, organizations — and say so in the prompt:
Before wiring anything to Axowl, read the relevant page with axowl_docs_list / axowl_docs_read.Do not infer how Axowl works from this repository, and do not invent URLs or endpoint shapes.If the documentation does not answer it, say so instead of guessing.This is not a formality. A model with a repository in front of it and no documentation will produce a confident, reasonable-looking answer for something like a sign-in URL — and it will simply not be a real address. The tool exists to remove that failure mode.
4. Work
Section titled “4. Work”A normal loop looks like this:
axowl_docs_list— when the work touches Axowl, read the page before you build against it.axowl_db_schema— never guess table names.axowl_db_query— read real rows. SELECT only; a failing query returns the database’s own error so your model can fix the statement.axowl_repo_list_files/axowl_repo_read_file— read what exists before changing it.- Your runtime writes the code.
axowl_propose_code_change— whole files, not patches. It opens a pull request.
The pull request is the only way code changes. There is no direct-push path, and merging stays a
human step. Paths are filtered — .github/, .env, key and certificate files are refused
individually, so one bad path does not sink the rest of the change. A pull request is capped at 20
files and 512KB.
Work that does not fit in one call
Section titled “Work that does not fit in one call”Most real work does not fit in 20 files. Build it up on one branch instead of proposing several disconnected pull requests:
- The first
axowl_propose_code_changereturns a branch name (axowl/agent/…). - Pass that branch back on every following call — the changes stack onto the same pull request.
- Read your own work back with
axowl_repo_read_file(path, branch)before writing the next batch. axowl_repo_pull_requeststells you whether it merged, whether it still merges cleanly, what CI concluded and what reviewers wrote.
Only branches the agent itself opened can be continued — naming your default branch is refused, because that would be a direct push wearing a different hat.
Set "delete": true on a file entry to remove a path. Deletions pass the same filter as writes.
When something is refused
Section titled “When something is refused”A refusal is a sentence, not an error, and it names the subject:
code — denied (agent lacks org.git_repository.propose_change)code — denied (delegating user lacks org.git_repository.propose_change)The second one matters. Authority is what the agent holds ∩ what you hold, so widening the agent will not lift it — you have to hold the scope too. A person cannot hand a machine authority they do not have themselves.
If every tool refuses, there is no delegating person on the session. axowl_whoami says so
directly.
Ask for it — the agent files an approval
Section titled “Ask for it — the agent files an approval”When the refusal names the agent, do not widen the role by hand. Have the agent ask:
axowl_request_permission("org.git_repository.propose_change", "Asked to open a PR on authhive-core; the test role is read-only.")→ submitted acme-2026-000031 — "Permission request: org.git_repository.propose_change" status : in_progress — 1 stamp(s) needed, in order: 1. Jane ParkAxowl finds the Access Approval form that grants the scope, submits a numbered, sealed document under the agent’s badge, and routes it up the agent’s reporting line — the person who hired it is its manager by default. The document shows up in Requests & Approvals with the agent as drafter. A person stamps it there with a passkey; the agent cannot. Then:
axowl_approval_status() → approvedaxowl_check_permission("org.git_repository.propose_change") → allowedaxowl_propose_code_change(...) → pull request openedTwo things have to be true for this to work, and the tool tells you which is missing:
- A form that grants the scope must exist. Under Forms & Policies, create an Access
Approval and pick the control — or, on an existing form, fill “Scope granted on approval”.
A form whose title merely mentions the scope grants nothing; the tool reports it as
document approval (grants nothing). - Someone above the agent must be able to approve it. The approver has to hold the scope
themselves (the ceiling rule in Capability grants). A machine
requester is never auto-approved, even on a risk-0 form — if nobody in its line qualifies, the
document stays
submittedandaxowl_approval_statussays so.
When the refusal names the delegating user, approval will not help — the grant lands on the
badge, not on the person. axowl_request_permission says so instead of filing a pointless request.
Turning it off
Section titled “Turning it off”Suspend or revoke the agent in the Agents tab. Its badge flips immediately and tokens stop working on the next call — revocation does not wait for an access token to expire. The TTL does the same thing on its own if you forget.
Both are recorded. A revoked agent’s roster row is retired, never erased.