Purserhouse is for US small and mid-size importers of goods made in China, and the customs brokers who serve them. Send a CBP Form 7501 for the IEEPA refund window and printed duty, or a demurrage and detention invoice for a check, the real last free day and a dispute basis. A person or company gives it their own documents; it returns every red line (anything that stops clearance or shipping), then at most three other findings on cost, risk or speed, and one next step. Agents can do this on the customer’s behalf.
The API contract below is a summary. /openapi.yaml is authoritative. The full guide written for language models is at /llms-full.txt
, and a short index is at /llms.txt
.
Machine-readable entry points
| What | Where |
|---|---|
| MCP server (Streamable HTTP, JSON-RPC 2.0) | https://purserhouse.com/mcp |
| REST API base | https://purserhouse.com/api/v1 |
| Public market snapshot (no key) | GET https://purserhouse.com/api/market |
| Free public tools (no key) | https://purserhouse.com/api/public/{refund-check,last-free-day,tariff-stack,screen,hs,dd-letter,isf-check} |
| OpenAPI description | /openapi.yaml |
| MCP server card | /.well-known/mcp.json |
| Short index for LLMs | /llms.txt |
| Full guide for LLMs | /llms-full.txt |
| Every page as Markdown | add index.md to any page URL, for example /en/importer/index.md |
The flow
- Look at coverage (no key). Call
GET /api/v1/coverageor the MCP toollanebrief_coverageto see lanes, ports, territories, container types, sides, document kinds and limits. - Get a key by requesting access (no key).
POST /api/v1/access-requestsorlanebrief_request_accesswith email, name, company, side, lanes and a note. The reply is202. A person at Purserhouse reviews the request; the key (lb_live_...) is issued after access is granted. - Grant consent (key). The customer agrees once that their documents may be processed.
POST /api/v1/consentorlanebrief_grant_consent. Before that, submissions fail with403 consent_required. - Submit documents (key).
POST /api/v1/casesorlanebrief_submit_documents. You get202with acase_id,status: "processing", apoll_urlandretry_after. Addwait_seconds(0 to 25) to hold the call until the case is answered: if it is ready in time you get200with theresultinline. Addidempotency_key(or rely on the files’ hashes) so that a retry returns the same case withduplicate: trueinstead of opening a second one. - Poll the result (key).
GET /api/v1/cases/{id}orlanebrief_get_result, no more often than everyretry_afterseconds, untilstatusisansweredorfailed. Not needed when the submit already returned theresult. - Optional: details.
GET /api/v1/cases/{id}/detailsorlanebrief_get_detailsreturns the detailed breakdown behind the findings.
GET /api/v1/account (or lanebrief_account) shows the plan, whether billing is enabled, the free cases per month, how many are used and how many remain.
MCP client configuration
Most MCP clients that support remote HTTP servers accept a block like this. Replace the key with your own.
{
"mcpServers": {
"lanebrief": {
"type": "http",
"url": "https://purserhouse.com/mcp",
"headers": {
"Authorization": "Bearer lb_live_..."
}
}
}
}
Tools: lanebrief_market_snapshot (no key), lanebrief_coverage, lanebrief_request_access (no key needed), lanebrief_grant_consent, lanebrief_submit_documents (files as base64), lanebrief_get_result, lanebrief_get_details, lanebrief_account; the free public tools (no key) lanebrief_refund_check, lanebrief_last_free_day, lanebrief_tariff_stack, lanebrief_screen, lanebrief_hs_rulings, lanebrief_dd_letter, lanebrief_isf_check.
REST examples
Coverage, no key:
curl https://purserhouse.com/api/v1/coverage
Request access, no key:
curl -X POST https://purserhouse.com/api/v1/access-requests \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","name":"A. Buyer","company":"Example Trading","side":"importer","lanes":["CN-PG"],"note":"Agent integration"}'
Grant consent, once per key:
curl -X POST https://purserhouse.com/api/v1/consent \
-H "Authorization: Bearer lb_live_..."
Submit documents as multipart:
curl -X POST https://purserhouse.com/api/v1/cases \
-H "Authorization: Bearer lb_live_..." \
-F "[email protected]" \
-F "[email protected]" \
-F "[email protected]" \
-F "lang=en" \
-F "dest_country=AE"
Or as JSON:
curl -X POST https://purserhouse.com/api/v1/cases \
-H "Authorization: Bearer lb_live_..." \
-H "Content-Type: application/json" \
-d '{"text":"Quote attached, 1x40HC Shanghai to Jebel Ali","lang":"en","dest_country":"AE","files":[{"name":"invoice.pdf","mime":"application/pdf","data_base64":"JVBERi0x..."}]}'
Poll:
curl https://purserhouse.com/api/v1/cases/CASE_ID \
-H "Authorization: Bearer lb_live_..."
Public market data (no key)
GET /api/market is a free, public JSON snapshot. It is described in /openapi.yaml (getMarket) and is also the MCP tool lanebrief_market_snapshot (no key, no arguments, same JSON). Responses are cached for 60 seconds, CORS is open, and requests are rate limited per IP (HTTP 429 with Retry-After).
curl https://purserhouse.com/api/market
It contains as_of, fx (exchange rates with date, change and source), lanes (a freight trend index per lane with value, unit, date and source), watch (which data sources were updated and when, never their numbers), attributions and note. A value appears only when its licence allows it. It never contains forwarder medians or customer data, and the trend index is a market trend, not a quote. Show the attributions when you reuse it. Sources and cadence: Data and sources
.
Free public tools (no key)
Seven JSON endpoints, described in /openapi.yaml (publicRefundCheck, publicLastFreeDay, publicTariffStack, publicScreen, publicHSRulings, publicDDLetter, publicISFCheck) and available as MCP tools without a key. They store nothing and call no model (except hs with q, which asks one for codes only); they run the same rules and licence gate as a customer case. Limit: 30 requests per 10 minutes per IP (screen: 10), then HTTP 429 with Retry-After. Errors are {"error": "..."}. Every response has sources and test_mode.
| Endpoint | MCP tool | Answers |
|---|---|---|
POST /api/public/refund-check | lanebrief_refund_check | Which chapter 99 lines of a CBP 7501 fall in the IEEPA refund window (in_refund_window, not_in_window, not_checked) and the printed duty total. Liquidation status is not on a 7501; interest is not included. |
GET /api/public/last-free-day | lanebrief_last_free_day | The last free day from the carrier’s published US tariff, or why it is not calculated. |
GET /api/public/tariff-stack | lanebrief_tariff_stack | The additional-duty layers in force for an HTS number and origin on a date. No total duty. |
POST /api/public/screen | lanebrief_screen | Possible matches of a company name on the sanctions lists. Not a clearance. |
GET /api/public/hs | lanebrief_hs_rulings | code: heading text from the US schedule and the newest 10 CBP CROSS rulings under a 6-digit heading. q (MCP query, a product description): up to 3 suggested codes (proposed by a model, each checked against the tariff tables, all text from the schedule) and up to 5 text matches, each with the newest 3 rulings; llm is unavailable when no model answered. Suggestions, not a classification; limit 10 per 10 minutes. |
POST /api/public/dd-letter | lanebrief_dd_letter | A demurrage or detention invoice check and letter drafts. |
POST /api/public/isf-check | lanebrief_isf_check | Which of the ten importer ISF elements (19 CFR 149.3(a)) are given or missing, and whether each HTSUS number has six digits and is in the HTS. Presence and format, not truth. Also takes the filled Excel template
as multipart field file (1 MB). |
curl -X POST https://purserhouse.com/api/public/refund-check -H 'Content-Type: application/json' \
-d '{"entry_date":"2025-12-01","origin":"CN","lines":[{"hts":"9903.01.24","duty":"1250.00"}]}'
curl 'https://purserhouse.com/api/public/last-free-day?carrier=MAEU&port=USLAX&arrival=2026-11-20&container=40'
Relay the JSON as returned. A null field, not_checked or computed: false means not calculated; do not fill it in.
What an answer looks like
The numbers below are an invented illustration of the shape, not a real result.
{
"case_id": 123,
"status": "answered",
"lang": "en",
"findings": [
{"code": "net_weight_mismatch", "axis": "risk", "severity": "blocker", "title": "Documents do not match", "detail": "Net weight differs between invoice and packing list.", "red_line": true, "evidence": {"invoice": "3400", "packing": "3120"}},
{"code": "quote_above_median", "axis": "cost", "severity": "medium", "title": "Quote above the market median", "detail": "...", "saving_usd": 250}
],
"next_step": "Ask the factory for a corrected packing list.",
"answer_text": "..."
}
If a number cannot be calculated from licensed data, the answer says “not calculated”. It is never filled in with a guess.
Errors
Errors have the shape {"error":{"code","message","hint"}}.
| Status | Code | What to do |
|---|---|---|
| 400 | invalid_request | Fix the request using hint. |
| 401 | unauthorized | Check the key and the Authorization header. |
| 402 | payment_required | Only once billing starts. Tell the customer; do not retry. |
| 403 | consent_required | Grant consent once, then resubmit. |
| 404 | not_found | The case id is wrong or the case was deleted after 7 days. |
| 413 | too_large | Send fewer or smaller files. |
| 429 | rate_limited | Wait for the Retry-After header, then retry. |
Rules agents must follow
- Send only the documents of the person or company you act for. Never send other people’s files.
- Never invent or round numbers in the answer. Quote the findings as given.
- Do not submit again while a case is processing. Poll instead.
- The result is guidance, not legal, customs or financial advice.
- Customer files are deleted after 7 days and are never used to train models.
Related
How Purserhouse differs from marketplaces, benchmarks and duty calculators: Purserhouse compared . The human version of the same service starts on the home page .
Frequently asked questions
Does Purserhouse have an MCP server?
Yes. It is a Streamable HTTP MCP server at https://purserhouse.com/mcp, using JSON-RPC 2.0 and the same bearer key as the REST API. The server card is at /.well-known/mcp.json.
Is there public data I can read without a key?
Yes. GET https://purserhouse.com/api/market returns exchange rates, a freight trend index per lane and the list of data sources with their last update time, as JSON. It has no customer data and no forwarder medians.
Do I need an API key?
Yes for customer answers. No key is needed for the coverage endpoint, the access request and the public /api/market snapshot. Keys look like lb_live_... and are issued after access is granted.
Does it cost anything?
Not at the moment: the service is free. Later there will be a monthly free allowance and paid use beyond it. The account endpoint reports the current billing state, so an agent never has to guess.
Can an agent pay for Purserhouse?
Not yet. Agent payments are planned but not available. Until billing starts, nothing is charged.
Which document should I send?
The customer's own commercial invoice, packing list, freight quote, bill of lading or certificates, as PDF, image or spreadsheet, for ocean container shipments only.
What if the answer is still processing?
Wait the number of seconds in retry_after and poll the same case again, or send wait_seconds (up to 25) with the submission to have the call held until the result is ready. Submitting the same documents again within 24 hours returns the same case, so a retry after a timeout is safe.
Updated: