Skip to content
billoquy

For agents · Model Context Protocol

Connect an assistant to billoquy

mcp.billoquy.com is an MCP server. Point an MCP-capable assistant at it and, on a signed-in person's behalf, it can discover the four checks, start one, read the finding with cited sources, preview a prepared fix, and hand the person off to approve and pay. It cannot approve, file, send, or pay — those are the person's actions.

Connect

The server is a streamable-HTTP MCP endpoint. Configure your client with the endpoint and the person's agent key as a bearer token:

Server namebilloquy (v1.0.0)
Endpointhttps://mcp.billoquy.com/mcp
Auth headerAuthorization: Bearer <agent key>

A typical client config looks like this:

{
  "mcpServers": {
    "billoquy": {
      "url": "https://mcp.billoquy.com/mcp",
      "headers": { "Authorization": "Bearer <agent key>" }
    }
  }
}

list_checks and the two resources need no auth, so a client can discover the surface before a key is attached. Every tool that touches an account needs the key. Clients that cannot send headers may instead pass the key as an accessToken argument on each account-scoped tool.

Auth: the agent key

An agent key is a billoquy session bound to a person's account with the purposeagent. It acts on that person's behalf: it is owner-scoped to their account and nothing else. It is the same token model the web app uses, with one added purpose — there is no parallel auth scheme.

  • Minted by the person, human-only. The signed-in account owner creates a key with POST /v1/agent-tokens. Minting is a human action — an agent cannot mint or revoke its own credentials. The key is returned once and never readable again.
  • Revoked by the person. DELETE /v1/agent-tokens revokes a key by value (owner-checked). See the developer API for both routes.
  • Fails cleanly when absent. An account-scoped tool called without a key returns an agent-legible error pointing to where the person creates one — never a silent or misleading result.

The approval guardrail

An agent can prepare — only the human approves

An agent connected to billoquy can discover checks, start them, read findings, preview a prepared fix, and hand the person a link to approve or pay. It can neverapprove, file, send, deliver, or pay. This is not a convention — it holds at three layers:

1. The edge (the real gate). Approving or declining a prepared fix requires ahuman token. An agent key — owner-scoped though it is — gets a 403 at the approval endpoint, no matter what any client does.
2. The MCP surface. There is simply no approve, decline, file, send, or pay tool. The client the tools are built on has no such method, so it cannot reach that endpoint.
3. Preview, then hand off. get_prepared_fix returns the drafted artifact and an approval link for the person to open in their own signed-in session — nothing more.

Tools

Seven tools. Only list_checks is unauthenticated.

ToolAuthWhat it does
list_checksnoneDiscover the four checks and the approval policy.
start_checkagent keyStart a free check for the owner.
get_checkagent keyPoll/read a check and its finding.
get_prepared_fixagent keyPreview a prepared fix + get the approval link.
start_fix_checkoutagent keyGet a Stripe Checkout link to hand the person.
get_billing_portalagent keyGet the self-serve billing portal link.
list_accountagent keyList the account's checks, fixes, and receipts.

list_checks

No input, no auth. Returns { checks, approvalPolicy }. Each entry inchecks carries id, name, tagline, available, finds, record, recordUrl, worth, input[], requiresDocument, documentHint and, when not available yet, anavailabilityNote.

→ list_checks {}

← {
  "checks": [
    {
      "id": "energy",
      "name": "Energy Check",
      "available": true,
      "finds": "the wrong rate plan",
      "record": "public utility rate database",
      "recordUrl": "https://apps.openei.org/USURDB/",
      "worth": "$200–600/yr",
      "input": [{ "name": "zip", "required": true, "description": "5-digit US ZIP code" }],
      "requiresDocument": true,
      "documentHint": "a photo or PDF of a recent utility bill"
    }
    // ...three more
  ],
  "approvalPolicy": "billoquy prepares; the person approves. ..."
}

start_check

Inputs: product (one of the four ids), intake (the structured fields for that check — see per-check intake), and for document-centric checks either documentUrl (a public https link to a PDF or photo) or uploadKey(already staged with billoquy). Optional accessToken.

  • A partial-coverage check (Medical) still starts, but if the person's hospital isn't loaded yet the diagnostic resolves to status: "unsupported" onget_check, with a registerInterestUrl — nothing is charged.
  • A document check with neither documentUrl nor uploadKey returns an error telling you to pass one (or have the person upload it).
  • On success: { status, diagnosticId, next }. Poll get_check with the id.
→ start_check {
  "product": "energy",
  "intake": { "zip": "78704" },
  "documentUrl": "https://example.com/my-utility-bill.pdf"
}

← { "status": "received", "diagnosticId": "diag_…", "next": "Poll get_check …" }

get_check

Input: diagnosticId (+ optional accessToken). The response depends on status:

  • complete: { status, product, headline, detail, dollars, confidence, worthFixing, citations }, where dollars is { amount, basis: "annual" | "one-time", currency: "USD" }or null, and each citation is { label, source, url }.
  • unsupported: { status, product, message, registerInterestUrl }.
  • failed: { status, product, reason }.
  • otherwise (still running): { status, product, note } — poll again shortly.
→ get_check { "diagnosticId": "diag_…" }

← {
  "status": "complete",
  "product": "energy",
  "headline": "You're on the wrong rate plan",
  "detail": "Switching to Plan B saves about …",
  "dollars": { "amount": 340, "basis": "annual", "currency": "USD" },
  "confidence": "high",
  "worthFixing": true,
  "citations": [
    { "label": "Utility tariff", "source": "OpenEI USURDB", "url": "https://apps.openei.org/USURDB/" }
  ]
}

get_prepared_fix

Input: diagnosticId (+ optional accessToken). If a fix has been drafted (the person paid), returns{ prepared: true, title, fixStatus, artifactPreview, approval } whereapproval is { required: true, by: "human", url, note } — the link the person opens to approve. If not, returns{ prepared: false, message, diagnosticPage }. This tool never approves.

start_fix_checkout

Input: diagnosticId (+ optional accessToken). Returns{ checkoutUrl, note } — a Stripe Checkout link to hand the person. The agent cannot pay; the person completes payment themselves, after which billoquy drafts the fix and it waits for their approval.

get_billing_portal

Input: optional accessToken. Returns { portalUrl, note } — the self-serve Stripe billing portal link for the person. No money moves through the agent.

list_account

Input: optional accessToken. Owner-scoped summary:{ user, diagnostics, preparedFixes, receipts } — diagnostics with status and finding headline, prepared fixes with their approval status (approvedVia), and receipts (whatWeDid, whatItMeans, whatHappensNext).

Per-check intake

The intake object for start_check, per product. Calllist_checks for the authoritative, machine-readable version.

productintake fieldsdocumentavailable
medicalzip (5-digit)Yes — an itemized hospital billrolling out
energyzip (5-digit)Yes — a utility billyes
internetzip (5-digit)Yes — an internet or TV billyes
fees(none)Yes — a bank or card statementyes

Resources

Two read-only resources describe the surface without any tool call:

  • billoquy://checks — the four checks as JSON (application/json): what each finds, its public record, inputs, and availability. The same data list_checksreturns.
  • billoquy://approval-policy — the "we prepare, you approve" guarantee in prose (text/plain): what an agent can and cannot do.

A worked example

Running an Energy Check for the connected person and handing them the payment and approval links:

  1. Connect with the person's agent key as a bearer token.
  2. list_checks → confirm energy is available and needs a document.
  3. start_check with { product: "energy", intake: { zip }, documentUrl } → a diagnosticId.
  4. get_check on that id until status: "complete" → the finding, with dollars and citations.
  5. If worthFixing: start_fix_checkout → hand the person checkoutUrl to pay.
  6. After they pay: get_prepared_fix → hand them approval.url. They approve on the web; the agent never does.

Honest limits

  • Availability. Medical depends on data loaded hospital by hospital, so a started check may come back from get_check as unsupported with aregisterInterestUrl. Relay it — do not lead the person into a dead end.
  • Polling. get_check is a single fetch; there is no server-side long-poll. Poll it until the status is terminal.
  • Documents. documentUrl must be a publicly fetchablehttps link (it is fetched under strict SSRF guards). Documents behind a login must be uploaded by the person; the tool's error copy says so.
  • Prefer raw HTTP? The MCP is a pure client of the same edge API — see thedeveloper API reference.