Skip to content
PAVE

The agent layer

The agent layer

Act with permission. Leave a trail.

PAVE is built so people can work through agents, safely. Borrowers and approved brokers can connect any AI assistant they trust — to read, write, chase, submit, and record — inside the same permissions and audit trail they already have.

How it works Begin as a borrower Book a broker demo

How a connection works

Two clicks. Real OAuth. Full power.

01

Paste

Paste your PAVE address — https://pave.legendhasit.co.nz/mcp/borrower for borrowers, https://pave.legendhasit.co.nz/mcp/broker for approved brokers — into the AI assistant of your choice.

02

Discover

The client makes one unauthenticated request and follows the OAuth flow your assistant already knows — no setup, no copy-pasting tokens.

03

Authorise

You're redirected here, signed in, and shown a single Authorize button on PAVE's branded consent screen. Click it.

04

Talk

Talk to your assistant. It reads, writes, and acts on your behalf. Every move shows up in Settings → AI agents. Revoke any time.

What the agent can do

Your agent can act for you — but never beyond you.

As a borrower

Read, decide, respond.

  • Confirm identity (WhoAmI) and find out who your broker is (GetMyBroker).
  • List and read your applications, see status, progress, offers.
  • Update your borrower profile (phone, address, preferred name).
  • Accept or decline an offer the bank has presented.
  • Leave notes that you and your broker can both see.
As an approved broker

Lead, submit, record.

  • Confirm identity and broker approval status (WhoAmI, GetMyBrokerProfile).
  • Triage your pipeline (ListMyBorrowers, ListBrokerApplications, GetBrokerApplication, GetBorrower).
  • Update applications and request information from a borrower.
  • Submit applications to banks; record offers as they arrive.
  • Leave notes that the borrower and broker can both see.
Never

Beyond your authority.

  • Reach data that belongs to another user — every tool re-authorises.
  • Change your role, status, or password.
  • Bypass the broker-approval gate.
  • Avoid the audit log.
Safe by default

Built so you can let an agent loose, then watch.

OAuth 2.1

Real authorization-code-with-PKCE flow. Dynamic client registration, branded consent. No copy-pasting tokens.

Token lifetime

Access tokens last 30 days. Refresh tokens last 90. A leaked token has a finite blast radius.

Audit trail

Every successful tool call is recorded with the actor, timestamp, IP, user agent, OAuth client, and metadata.

Email signal

Email the moment a new agent is authorised — same pattern as "you logged in from a new device".

Significant actions

Email when the agent accepts an offer, declines an offer, or submits an application to a bank.

Instant revoke

Per-token revoke from Settings → AI agents. Master kill switch disables all agent access.

Rate-limited

120 requests per minute per token. A runaway client cannot starve the platform.

Defence in depth

Route middleware (auth, role, mcp.enabled, broker-approval, throttle) plus per-record Eloquent policies inside every tool.

For developers

Technical reference. Spec-compliant. No surprises.

Everything below is the proof under the pitch — endpoints, OAuth flow, RFC references, MCP versions, worked examples, and tool notes for the people building agents on PAVE.

Endpoints
Audience URL Auth
Borrower https://pave.legendhasit.co.nz/mcp/borrower Bearer (borrower token)
Broker https://pave.legendhasit.co.nz/mcp/broker Bearer (approved broker token)
DCR https://pave.legendhasit.co.nz/oauth/register Open (RFC 7591)

Single OAuth scope: mcp:use. Borrower-vs-broker separation is the URL plus role middleware. A borrower's token gets 403 at /mcp/broker, and vice versa.

Supported MCP versions

The server advertises support for MCP protocol versions 2025-11-25, 2025-06-18, 2025-03-26, and 2024-11-05. It picks the latest the client requests during initialize.

Worked example — full handshake
# 1. Unauthenticated MCP call → 401 with discovery pointer
curl -i -X POST https://pave.legendhasit.co.nz/mcp/borrower \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp",
  resource_metadata="https://pave.legendhasit.co.nz/.well-known/oauth-protected-resource/mcp/borrower"

# 2. Discover the protected-resource metadata, then the auth server
curl -s https://pave.legendhasit.co.nz/.well-known/oauth-protected-resource/mcp/borrower
# → { "resource":"…", "authorization_servers":["https://pave.legendhasit.co.nz"], "scopes_supported":["mcp:use"] }
curl -s https://pave.legendhasit.co.nz/.well-known/oauth-authorization-server
# → { "issuer":"…", "authorization_endpoint":"https://pave.legendhasit.co.nz/oauth/authorize",
#     "token_endpoint":"https://pave.legendhasit.co.nz/oauth/token",
#     "registration_endpoint":"https://pave.legendhasit.co.nz/oauth/register",
#     "code_challenge_methods_supported":["S256"],
#     "scopes_supported":["mcp:use"],
#     "grant_types_supported":["authorization_code","refresh_token"] }

# 3. Dynamically register your client
curl -s -X POST https://pave.legendhasit.co.nz/oauth/register \
  -H 'Content-Type: application/json' \
  -d '{
    "client_name": "Your assistant",
    "redirect_uris": ["https://your-client.example/oauth/callback"]
  }'
# → { "client_id":"…", "grant_types":["authorization_code","refresh_token"],
#     "redirect_uris":["…"], "scope":"mcp:use",
#     "token_endpoint_auth_method":"none" }

# 4. PKCE authorization-code flow
# 4a. Open the authorize URL in the user's browser:
#     https://pave.legendhasit.co.nz/oauth/authorize
#       ?response_type=code
#       &client_id=…
#       &redirect_uri=https%3A%2F%2Fyour-client.example%2Foauth%2Fcallback
#       &scope=mcp%3Ause
#       &code_challenge=…
#       &code_challenge_method=S256
#       &state=…
# 4b. The user lands on the branded "Authorize" screen. After click, we
#     redirect to redirect_uri with ?code=…&state=…
# 4c. Exchange the code:
curl -s -X POST https://pave.legendhasit.co.nz/oauth/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=authorization_code&client_id=…&code=…&code_verifier=…&redirect_uri=https%3A%2F%2Fyour-client.example%2Foauth%2Fcallback'
# → { "token_type":"Bearer", "expires_in":2592000,
#     "access_token":"eyJ…", "refresh_token":"…" }

# 5. Use the bearer token
curl -s -X POST https://pave.legendhasit.co.nz/mcp/borrower \
  -H 'Authorization: Bearer eyJ…' \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Desktop clients that use a custom-scheme callback (your-app://oauth/callback) work too — schemes are configured server-side per-deployment.

Worked example — calling a tool
# Request
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "ListMyApplications",
    "arguments": {"limit": 5}
  }
}

# Response (success)
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{
      "type": "text",
      "text": "{\"count\":1,\"applications\":[{\"id\":42,\"reference\":\"MTG-AB12CD\",\"status\":\"submitted_to_broker\",\"loan_amount_requested\":\"750000.00\",…}],\"next_after_id\":null}"
    }],
    "isError": false
  }
}

# Response (tool-level error — authorisation, validation, missing record)
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{ "type":"text", "text":"This action is unauthorized." }],
    "isError": true
  }
}

Every JSON tool returns its payload as a single text content item containing JSON-encoded data. Parse the inner string. Tool-level isError: true means a controlled failure (auth, validation, not-found); do not retry on those.

Worked example — reading a resource
# List available resources
{ "jsonrpc":"2.0", "id":3, "method":"resources/list" }
# → result.resources contains entries like:
# {
#   "name": "my-profile-resource",
#   "title": "My Profile Resource",
#   "uri": "pave://borrower/me",
#   "mimeType": "application/json",
#   "description": "The authenticated borrower's profile…"
# }

# Read one
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "resources/read",
  "params": { "uri": "pave://borrower/me" }
}
Resources we expose
URI Server What it returns
pave://borrower/me Borrower The authenticated borrower's profile snapshot.
pave://borrower/banks Borrower Active NZ banks PAVE works with (id, name, slug).
pave://broker/banks Broker Same plus contact email — pass id to SubmitToBank.
Prompts we expose
Name Server What it guides
StartApplicationPrompt Borrower An eight-step interview that collects everything a broker needs to start an application.
PipelineSummaryPrompt Broker A weekly pipeline review: bucket by status, surface what needs the broker's action.

Use prompts/list and prompts/get to fetch them. The body of each is a system-prompt-style instruction the agent can adopt for the workflow.

Pagination

List tools (ListMyApplications, ListBrokerApplications) accept limit and after_id. Every response includes a next_after_id — pass it back to fetch the next page; null means end-of-list.

Errors

Authorisation and validation failures return a tool-level error: {"isError": true, "content": [{"type":"text","text":"…"}]}. Don't retry on auth errors — surface the message to the user. Server errors come back as JSON-RPC error code -32603.

Notes & caveats
  • Application creation is broker-led. Borrowers can edit applications in draft or needs_borrower_info via UpdateMyProfile + AddNote for context, but creating the application record happens through their broker. The StartApplicationPrompt guides agents to collect detail and hand it to the broker via a note.
  • Notes have no visibility flag. Every note on an application is visible to both the borrower and the assigned broker. Phrase accordingly.
  • File uploads aren't supported via MCP yet. Borrowers and brokers upload supporting documents through the web UI (Settings or the application page).
  • Streaming responses aren't used. Every tool returns a single response object; no notifications are emitted while a tool runs.
  • RecordOffer uses decimal interest rates. 0.0625 is 6.25%, not 6.25. The schema rejects values above 1.
For agents reading this page
  • Your authority is scoped to the user who authorised you. Don't try to access another user's data — the server will return errors.
  • Tool descriptions are authoritative. Read each tool's description and input schema before calling.
  • Prefer reading first. WhoAmI followed by a list tool gives you full context before you make any changes.
  • Never guess identifiers. References (MTG-XXXXXX), offer ids, bank ids, submission ids — they all come from list/get tools or resources.
  • Significant actions trigger an email to the user. They will know immediately if you act unexpectedly.
For borrowers

Start the application. Let your agent keep the thread.

Once you're signed up, paste your borrower URL into the assistant of your choice and ask it where you're up to.

Begin your application
For brokers

See PAVE in motion. Run it through your agent.

Thirty-minute walkthrough. Broker workspace, borrower journey, agent layer.