Skip to content

Developers

MCP server

Demurly has an MCP (Model Context Protocol) server, so an AI assistant such as Claude can look up your records, disputes, invoices and statistics for you, and draft comments and disputes. It never sends anything: a person at your company submits each draft in the app.

On this page

Connect

Make an API key under Settings > API keys with the scopes you want the assistant to have (records:read for records, disputes and invoices; analytics:read for statistics; and so on). A test key (dmrl_test_...) connects it to your test-mode sandbox instead of your real data. The server is at:

https://www.demurly.com/api/mcp

In Claude Code:

claude mcp add --transport http demurly https://www.demurly.com/api/mcp \
  --header "Authorization: Bearer dmrl_your_key"

Any MCP client that supports Streamable HTTP with a custom header works the same way: send the key as Authorization: Bearer dmrl_.... Connecting from Claude's apps (claude.ai and Claude Desktop custom connectors) needs sign-in with OAuth, which Demurly doesn't offer yet.

Tools

Each tool needs a scope on the key, as the matching API endpoint does. Without it, the tool answers that it can't run.

search_records (records:read)
Demurrage records (each a driver's wait billed as a charge), newest first: status, times, billed minutes, rate, amount, the terminal, carrier and fuel buyer, GPS evidence summary. Filter by text, status, terminal, the other company, start date range, amount or whether it was disputed.
get_record (records:read)
One demurrage record by id, optionally with its history (events: created, sent, disputed, approved, paid, comments...).
get_record_facts (records:read)
What the evidence says about one record, in plain sentences (the wait, how it ended, where the times came from, GPS, photos, the charge, adjustments, disputes), plus review flags with their reasons (GPS gaps or jumps, missing photos, times that don't add up, a wait long for its terminal). Built from the record's data, not guessed: state only these facts when writing about a record.
list_disputes (records:read)
Records the fuel buyer disputed, newest first, each with the dispute's reason, type, proposed amount, when it was opened and escalated, and how many rounds. state: open (default), resolved or all.
get_dispute (records:read)
One record's dispute (id is the record's id): the record, a summary, and the dispute's events in order (disputes, adjusted times, resends, escalations, accepted offers, approvals, comments).
list_invoices (records:read)
Approved charges as invoices (each approved charge is its own invoice; the record number is the invoice number), newest first. status: unpaid, payment_pending, paid or closed; default all.
get_invoice (records:read)
One invoice (id is the record's id). Not found until the charge is approved.
list_terminals (terminals:read)
The terminals your company uses, with their ids (for filtering records and stats by terminal).
list_connections (connections:read)
The companies you're connected to (fuel buyers for a carrier, carriers for a buyer), with their ids (partner_id in other tools).
list_drivers (drivers:read)
Your company's drivers (carriers only).
list_saved_reports (reports:read)
Reports your team saved in the report builder: their columns and filters.
stats_by_terminal (analytics:read)
For waits that started in the range: count, total and average wait minutes, billable minutes, billed and approved dollars, by terminal.
stats_by_partner (analytics:read)
For waits that started in the range, by the other company on the record (a carrier's fuel buyers, a buyer's carriers): count, how many were disputed, total and average wait minutes, billed and approved dollars. Most disputed first.
stats_by_period (analytics:read)
For waits that started in the range, by week or month on the company's calendar: count, wait minutes, billed, approved and paid dollars.
stats_aging (analytics:read)
What's outstanding now by age (0-30, 31-60, 61-90, 90+ days): charges awaiting review (since sent) and approved but unpaid (since approved), with counts and dollars.
draft_comment (records:write)
Draft a comment on a record. It isn't posted: it waits on the record in the Demurly app until someone at your company submits it (it's then posted as them) or discards it. Returns the draft and a review_url to share with the user.
draft_dispute (records:write)
Fuel buyers: draft a dispute of a charge waiting for review (sent_to_customer): the reason, optionally its type, the minutes disputed and an offer below the charge. It isn't sent: someone at your company reviews and submits it in the Demurly app. Returns the draft and a review_url.
list_drafts (records:read)
Drafts (comments, disputes) waiting for someone at your company to submit or discard, newest first; on one record or all.

Drafts

With records:write, an assistant can draft a comment on a record, or (fuel buyers) a dispute of a charge waiting for review. A draft isn't posted or sent: it appears on the record in the app as “Drafts from your AI assistant”, naming the API key that wrote it, until someone at your company sends it (it's then done as them, with the same checks as the app's own buttons) or discards it. A record holds at most 10 drafts waiting; test keys can't draft.

How it works

The tools are the API's own endpoints: the same records, the same company and side, and the same checks on every argument (a bad value comes back as a tool error the assistant can read and fix). Calls count against the key's rate limit and appear in its usage as mcp:<tool>. Amounts are US dollars, times are UTC ISO 8601, and lists are newest first with a cursor, as in the API reference.

The server is stateless Streamable HTTP: it answers each POST with JSON (no event stream or sessions), supports protocol versions 2024-11-05 to 2025-11-25, and refuses requests sent by web pages on other sites.

NextConventions and policiesTimes and time zones, money, IDs, idempotency, versioning, testing and browser use.