Agent API
Pair an AI agent to vet and seal under your account.
Agent API
If you have an AI agent (Claude, ChatGPT, a custom script, anything that can make HTTP calls), you can pair it with your TDPort account so it can vet carriers and seal cryptographic evidence records on your behalf. The output is a URL you click to view the sealed cert in your browser.
There are three ways in, ordered from simplest to most flexible:
- One-shot API key (recommended). Generate a
tdpk_…key on/account, hand it to any AI, the AI does one HTTP POST and gets back a sealed URL. No client install. The server holds the agent's signing key. - MCP server. Same end result, but with streaming and client-side key custody. Best for Claude Desktop / Cursor / Codex if you already use MCP for other services.
- Raw HTTP API (advanced). The three-call protocol the MCP wraps. Use this if you're writing a custom script that wants client-held signing keys and tight control over each step.
Option 1 — One-shot API key (recommended)
Sign in at tdport.io, open Account → Connect an
AI agent, give it a name, click Generate API key. Copy the
tdpk_… value. That's it — no other setup.
Hand the key to any AI agent (Claude Desktop, Claude Code, ChatGPT, Cursor, Codex, a curl script — anything that can make HTTP calls):
Vet MC-1061559 on tdport.io using this API key:
tdpk_…
The agent posts to /api/agent/vet:
POST https://tdport.io/api/agent/vet?mc=1061559
Authorization: Bearer tdpk_…
…and receives:
{
"ok": true,
"url": "https://tdport.io/v/c-…#k=…",
"record_id": "c-…",
"carrier": {
"mc_number": "1061559",
"dot_number": 3331119,
"legal_name": "LAWLEYS TRUCKING LLC"
},
"row_count": 10,
"vetted_at": "2026-05-19T23:40:26.896Z"
}
The url is the deliverable — it opens a page that renders the full
sealed vetting in the user's browser. Server-side: TDPort holds the
agent's Ed25519 keypair (per agent, scoped to your account) and signs
the seal on the agent's behalf. Revoking the key from /account
immediately cuts off that agent.
A bare-curl example:
curl -X POST \
-H "Authorization: Bearer tdpk_…" \
"https://tdport.io/api/agent/vet?mc=1061559"
This is the cleanest path for "any AI in the wild." If you want client-side key custody (the user's machine signs, TDPort never holds the private key), use Option 2 or Option 3.
Option 2 — MCP server
The server tarball lives at https://tdport.io/mcp/tdport-mcp-latest.tgz.
npx will fetch and run it on demand — no global install. In your MCP
client config:
{
"mcpServers": {
"tdport": {
"command": "npx",
"args": ["-y", "--package=https://tdport.io/mcp/tdport-mcp-latest.tgz", "tdport-mcp"]
}
}
}
For Claude Desktop on macOS this file lives at
~/Library/Application Support/Claude/claude_desktop_config.json. For
Claude Code, it's the project or user .mcp.json. Restart the MCP
client after editing.
The server exposes three tools:
tdport_pair— one-time setup. The user signs in at tdport.io, opens Account → Agent APIs → Pair via token, copies thetdpp_…token, and gives it to the agent. The agent callstdport_pair({ token })once, and credentials are written to~/.tdport-agent/default.json.tdport_lookup_carrier— resolves a name or MC/USDOT to a specific carrier. Used to disambiguate before vetting.tdport_vet_and_seal— runs the full FMCSA + USPS vetting, seals the bundle on the TDPort chain, and returns ahttps://tdport.io/v/<id>#k=<key>URL. The tool's description tells the agent to return that URL as the final answer.
To pin a specific version, swap latest for the version (e.g.
tdport-mcp-0.1.0.tgz). Published tarballs are immutable; latest
follows the most recent release. The MCP server's source is in the
TDPort repo at mcp-server/ and is AGPL-3.0 licensed.
If you want to use a different credential slot (e.g., one identity per
project), pass slot: "<name>" in tool calls, or set
TDPORT_AGENT_SLOT=<name> in the MCP server's env block.
Option 3 — Raw HTTP API (advanced)
If you are not using MCP, talk to the HTTP API directly. The protocol below is what the MCP server wraps; the two paths are wire-compatible.
Getting credentials
In your account, click Agent APIs, name the agent, hit Generate credentials. The dashboard shows you:
producerCode— public handle on the ledger (e.g.TDP-A1B2C3D4)producerId— full Ed25519 pubkey (ed25519:<64-hex>)privateKeyHex— 64 hex chars; the only copy is the one you takeapiKey— opaque string starting withtdpk_baseUrl—https://tdport.io
Hit Copy it all or Download <agent>.json. The credentials are shown exactly once; TDPort never sees the private key.
Authentication
Every API call MUST include:
Authorization: Bearer <apiKey>
A bearer that doesn't resolve to an active agent returns 401. Calls
without a bearer fall through to the anonymous tier (no sealing).
Endpoints
GET /api/quick-check?docket=<query>
Resolve a carrier by MC#, USDOT#, 10-digit phone, or name prefix.
GET https://tdport.io/api/quick-check?docket=KMR%20Trucking
Authorization: Bearer tdpk_...
Returns { ok: true, matches: [...] }. If a name resolves to multiple
carriers, the agent should pick one (or hand the choice back to the user).
GET /api/vet?mc=<mc> (NDJSON stream)
Stream the full FMCSA + USPS vetting. One JSON event per line:
{"event":"row","row":"identity","status":"ok","data":{...},"source":{...}}
{"event":"row","row":"authority","status":"alert","data":{...},"source":{...}}
...
{"event":"done","mcNumber":"...","vettedAt":"...","dotNumber":...}
Read all row events into an array; the done event marks end-of-stream.
POST /api/v2/seal
Seal a vetting bundle onto the chain. This is the cryptographic step.
POST https://tdport.io/api/v2/seal
Authorization: Bearer tdpk_...
Content-Type: application/json
X-App-Db-Meta: {"type":"carrier-vetting","carrierMc":"<MC>"}
{
"submission": {
"record_id": "c-<base36(now)>-<8 hex chars>",
"record_type": "carrier_vetting",
"package_hash": "sha256:<hex>",
"producer_id": "ed25519:<your pubkey hex>",
"title": "<carrier name and MC>",
"application_metadata": { "type": "carrier-vetting" }
},
"producer_signature": "ed25519:<hex sig of canonicalized submission>",
"encrypted_blob": "<base64 of (iv || ciphertext)>"
}
The server validates the bearer's producer matches producer_id, forwards
to the chain, and returns { record_id, verify_url, ... }.
The seal recipe, step by step
Build a
VettingBundleplaintext (JSON):{ "version": 1, "vettingId": "v-<16 hex chars>", "vettedAt": "<ISO timestamp from /api/vet 'done' event>", "broker": { "producerId": "<your producerId>", "displayName": "<your agent name>" }, "carrier": { "mcNumber": "...", "dotNumber": 12345, "legalName": "..." }, "load": null, "rows": [ /* every row event from /api/vet */ ], "tests": [], "generatedBy": "tdport-agent/<your agent name>" }Canonicalize that bundle using RFC 8785 JSON Canonicalization Scheme (npm:
canonicalize). UTF-8 encode the canonical string to get the plaintext bytes.Generate a random 256-bit AES-GCM key and a random 12-byte IV. Encrypt the plaintext bytes. Concatenate
blob = iv || ciphertext.package_hash = "sha256:" + sha256_hex(blob).record_id = "c-" + base36(Date.now()) + "-" + 8 random hex chars.Build the
submissionobject (see endpoint above). Canonicalize it.Sign the canonical submission bytes with Ed25519 using your
privateKeyHex.producer_signature = "ed25519:" + hex(signature).POST
{ submission, producer_signature, encrypted_blob: base64(blob) }to/api/v2/sealwith the bearer header.The server returns
record_id. The final output the agent should hand back to the user:https://tdport.io/v/<record_id>#k=<base64url(aes_key)>The
#k=fragment is the AES decryption key. URL fragments never leave the user's browser — clicking the link opens the verifier, which decrypts client-side and shows the cert. Never log or post the decryption key anywhere except this URL.
Reference implementations
Two live in the TDPort source tree, both wire-compatible with the protocol above:
mcp-server/— the npm-published MCP server (Option 1). Most agents should use this rather than reimplement the protocol. Sources are the spec for tool shape and the contract the agent sees.scripts/agent-example.mts— a single-file CLI that does the same recipe end-to-end:pnpm tsx scripts/agent-example.mts pair --token=<one-time> pnpm tsx scripts/agent-example.mts vet MC-123456 --name=<slot>
Both use @noble/curves for Ed25519, Node's webcrypto for AES-GCM,
and the canonicalize package for RFC 8785. Read either as the spec
if anything on this page is ambiguous.
What the agent should NOT do
- Don't summarize the cert as your final output. The user wants the URL so they can view the sealed cert in their browser. The summary is optional context; the URL is the deliverable.
- Don't skip the seal. The whole point of paired-agent access is the
cryptographic evidence record. Bare data from
/api/vetis the anonymous tier — your user can already get that without you. - Don't make a value judgment. TDPort surfaces FMCSA's own labels. Don't say "looks risky" or "I'd recommend hire." Surface the flags; let the user decide.
- Don't store the decryption key server-side or in logs. The key lives in the URL fragment only. If you persist anywhere, you've defeated the design.
Errors
| Status | Meaning |
|---|---|
401 |
API key missing, malformed, or revoked. Stop and report. |
403 |
The body's producer_id doesn't match your API key. Bug in your code. |
404 |
Carrier not found (/api/quick-check) or record not found. |
409 |
Producer key already registered (pairing collision; extremely rare). |
429 |
Rate-limited — back off. |
502 |
ProofChain unreachable. Retry with backoff. |
Revoking an agent
The account owner can revoke any paired agent from the Agents panel
on /account. Revocation is immediate; the next API call returns 401.
Past sealed records remain valid (they were valid at the time signed).