{"slug": "how-an-ai-agent-discovers-a-3pl-over-x402", "title": "How an AI agent discovers a 3PL over x402", "summary": "Nettrux has published a machine-readable x402 discovery pointer at its .well-known endpoint, making it the first 3PL to expose freight-booking discovery to AI agents over HTTP. The live document points agents to an agent guide and a 126-path OpenAPI spec on the freight API host, but its resources array is empty and three deploy gates — discovery publishing, real-money settlement, and guest checkout — remain false, so no public pay-and-book endpoint exists yet. Nettrux says it has settled a real x402 USDC payment on Base in testing, which it distinguishes from a live public rail.", "body_md": "Agent builders keep asking the same first question: *where does my agent look?*\n\nNettrux is the first 3PL on x402, so the answer is deliberately boring and HTTP-native. Before any quote, before any payment header, an agent should fetch three public documents:\n\n`https://www.nettrux.ai/.well-known/x402`\n`https://www.nettrux.ai/agents`\n`https://nettrux-edge.fly.dev/openapi.json`\nThis article is a how-to. Every snippet below was run against the live URLs on Nov 9, 2026 (America/Los_Angeles). The outputs are real. The gates are still off — and we say that in the same breath as the discovery story.\n\n**You will get:** a machine-readable pointer, an agent guide, a 126-path OpenAPI for the freight API host, and a clear list of deploy gates.\n\n**You will not get:** a live public pay-and-book endpoint, a populated `resources` array, public real-money settlement, or guest checkout. Those three switches are baked false:\n\n`EDGE_X402_DISCOVERY_PUBLISHED`: false` X402_REAL_MONEY_SETTLEMENT_LIVE`: false` GUEST_X402_CHECKOUT_ENABLED`: false\nNettrux has settled a real x402 USDC payment on Base in testing. That is not the same as a public endpoint. Keep reading if you are wiring discovery into an agent today; stop if you expected a stranger wallet to book a truck without a human and a bearer token.\n\nx402 discovery starts at `/.well-known/x402` on the site an agent already trusts. On Nettrux that document is a **pointer**, not a payable catalog.\n\n``` python\nimport json\nimport urllib.request\n\nDISCOVERY = \"https://www.nettrux.ai/.well-known/x402\"\n\nreq = urllib.request.Request(\n    DISCOVERY,\n    headers={\n        \"User-Agent\": \"nettrux-discovery-howto/1.0\",\n        \"Accept\": \"application/json\",\n    },\n)\nwith urllib.request.urlopen(req, timeout=20) as r:\n    doc = json.load(r)\n\nprint(doc[\"name\"], doc[\"kind\"], doc[\"x402Version\"])\nprint(\"resourceHost:\", doc[\"resourceHost\"])\nprint(\"openapi:\", doc[\"openapi\"])\nprint(\"resources:\", doc[\"resources\"])\nprint(\"gates:\", doc[\"gates\"])\nprint(\"payment.claimed:\", doc[\"payment\"][\"claimed\"])\n```\n\nLive output from that run:\n\n```\nNettrux resource-server 1\nresourceHost: https://nettrux-edge.fly.dev\nopenapi: https://nettrux-edge.fly.dev/openapi.json\nresources: []\ngates: {\n  \"EDGE_X402_DISCOVERY_PUBLISHED\": false,\n  \"GUEST_X402_CHECKOUT_ENABLED\": false,\n  \"X402_REAL_MONEY_SETTLEMENT_LIVE\": false\n}\npayment.claimed: False\n```\n\nRead that carefully:\n\n`resources` is `payment.claimed` is `resourceHost` and `openapi` tell the agent where the freight API lives.`gates` are the same three booleans as `llms.txt` and the agent guide.\nThe pointer also lists a helper array, `x402Resources`, that names the documented commit route on the freight API host. That entry is catalog metadata for builders. It is **not** a claim that the public rail settles money today:\n\n```\nfor res in doc.get(\"x402Resources\") or []:\n    print(res[\"method\"], res[\"url\"])\n    print(res[\"description\"][:160], \"...\")\n```\n\nLive output:\n\n```\nPOST https://nettrux-edge.fly.dev/v1/immediate-bookings/commit\nBearer plus {quote_id}. The OpenAPI summary documents an x402 v1 402 challenge\nwhen X-PAYMENT is absent, and verify, then settle, then book when it is present.\nThis entry does not name an asset, a chain, or a payee. ...\n```\n\nThe pointer names `https://nettrux-edge.fly.dev/.well-known/x402` as the freight API discovery document. While `EDGE_X402_DISCOVERY_PUBLISHED` is false, that URL is **not** published by the API host. A 404 there is expected; the site pointer is still the catalog.\n\n``` python\nimport urllib.error\n\nEDGE_DISCOVERY = \"https://nettrux-edge.fly.dev/.well-known/x402\"\nreq = urllib.request.Request(\n    EDGE_DISCOVERY,\n    headers={\"User-Agent\": \"nettrux-discovery-howto/1.0\", \"Accept\": \"application/json\"},\n)\ntry:\n    urllib.request.urlopen(req, timeout=20)\nexcept urllib.error.HTTPError as e:\n    print(\"Edge discovery HTTP\", e.code)\nEdge discovery HTTP 404\n```\n\nIf your agent treats every 404 as \"this 3PL does not support x402,\" you will miss Nettrux. Check the **site** pointer first. Only treat Edge discovery as authoritative once `discoveryPublished` flips to true and the gate says so.\n\nThe discovery pointer points at the OpenAPI. That document is the request/response contract for every authenticated call. It is public and needs no token.\n\n```\nOPENAPI = \"https://nettrux-edge.fly.dev/openapi.json\"\nreq = urllib.request.Request(\n    OPENAPI,\n    headers={\"User-Agent\": \"nettrux-discovery-howto/1.0\", \"Accept\": \"application/json\"},\n)\nwith urllib.request.urlopen(req, timeout=20) as r:\n    oa = json.load(r)\n\npaths = oa[\"paths\"]\nprint(oa[\"info\"][\"title\"], \"openapi\", oa[\"openapi\"], \"paths\", len(paths))\n\ncommit = paths[\"/v1/immediate-bookings/commit\"][\"post\"]\nprint(\"summary:\", commit[\"summary\"])\nprint(\"security:\", commit[\"security\"])\nprint(\"responses:\", list(commit[\"responses\"]))\nbody = commit[\"requestBody\"][\"content\"][\"application/json\"][\"schema\"]\nprint(\"required body fields:\", body[\"required\"])\nNettrux Edge API openapi 3.0.3 paths 126\nsummary: Commit an immediate booking by PAYING for it over the x402 v1 handshake\n(402 challenge without X-PAYMENT; verify → settle → book with it)\nsecurity: [{'bearerAuth': []}]\nresponses: ['200']\nrequired body fields: ['quote_id']\n```\n\nA few agent-relevant paths that exist today:\n\n| Path | Methods | Auth | \n|---|---|---|\n| `/health` ,`/ready` | GET | none | \n| `/v1/rates/shop` | POST | bearer | \n| `/v1/quotes` | GET, POST | bearer | \n| `/v1/immediate-bookings/commit` | POST | bearer | \n| `/v1/me` | GET | bearer | \n| `/v1/agent/approvals` | GET | bearer | \n\nThe OpenAPI security scheme is HTTP bearer (`JWT or API key`). The public site does **not** issue tokens and does not describe self-serve signup. Guest checkout routes appear in the OpenAPI and stay flagged off — do not call them from these instructions.\n\nUnauthenticated health is a useful smoke check that the freight API host is up:\n\n```\nHEALTH = \"https://nettrux-edge.fly.dev/health\"\nreq = urllib.request.Request(\n    HEALTH,\n    headers={\"User-Agent\": \"nettrux-discovery-howto/1.0\", \"Accept\": \"application/json\"},\n)\nwith urllib.request.urlopen(req, timeout=20) as r:\n    health = json.load(r)\nprint(health[\"ok\"], health[\"data\"][\"status\"])\nTrue healthy\n```\n\nCommit without a bearer is rejected. Same day check:\n\n``` python\nimport urllib.error\n\nurl = \"https://nettrux-edge.fly.dev/v1/immediate-bookings/commit\"\npayload = json.dumps({\"quote_id\": \"00000000-0000-4000-8000-000000000000\"}).encode()\nreq = urllib.request.Request(\n    url,\n    data=payload,\n    method=\"POST\",\n    headers={\n        \"Content-Type\": \"application/json\",\n        \"User-Agent\": \"nettrux-discovery-howto/1.0\",\n    },\n)\ntry:\n    urllib.request.urlopen(req, timeout=20)\nexcept urllib.error.HTTPError as e:\n    print(\"HTTP\", e.code, e.read().decode()[:180])\nHTTP 401 {\"ok\":false,\"data\":null,\"error\":{\"code\":\"missing_token\",\n\"message\":\"Authorization: Bearer <token> is required\"}, ...}\n```\n\nThat is the boundary: discovery is public; booking is authenticated; minting still requires a named human on the Nettrux side of the product.\n\nAfter the JSON pointer, have the agent (or its author) read the prose contract:\n\nBoth returned HTTP 200 when we checked them for this article. The guide's instructions match the pointer: price-lock with `POST /v1/quotes` (`quoteNumber`, `validUntil`, `rateInput`), then call `POST /v1/immediate-bookings/commit` with `{ \"quote_id\": \"...\" }` and **no** `X-PAYMENT` header for the documented 402 challenge. A paid retry carries the `X-PAYMENT` header an x402 v1 client builds from that challenge.\n\nBecause `X402_REAL_MONEY_SETTLEMENT_LIVE` is false, treat that handshake as **documented**, not as a claim that a public payment settles or books freight today.\n\n``` python\ndef discover_nettrux():\n    \"\"\"Public discovery only. Does not quote, book, or pay.\"\"\"\n    import json, urllib.request, urllib.error\n\n    pointer = json.load(urllib.request.urlopen(\n        urllib.request.Request(\n            \"https://www.nettrux.ai/.well-known/x402\",\n            headers={\"Accept\": \"application/json\",\n                     \"User-Agent\": \"nettrux-discovery-howto/1.0\"},\n        )\n    ))\n    gates = pointer[\"gates\"]\n    openapi = json.load(urllib.request.urlopen(\n        urllib.request.Request(\n            pointer[\"openapi\"],\n            headers={\"Accept\": \"application/json\",\n                     \"User-Agent\": \"nettrux-discovery-howto/1.0\"},\n        )\n    ))\n\n    return {\n        \"name\": pointer[\"name\"],\n        \"resource_host\": pointer[\"resourceHost\"],\n        \"resources_empty\": pointer[\"resources\"] == [],\n        \"gates\": gates,\n        \"openapi_title\": openapi[\"info\"][\"title\"],\n        \"openapi_paths\": len(openapi[\"paths\"]),\n        \"commit_documented\": \"/v1/immediate-bookings/commit\" in openapi[\"paths\"],\n        \"ready_for_public_pay_and_book\": (\n            gates[\"EDGE_X402_DISCOVERY_PUBLISHED\"]\n            and gates[\"X402_REAL_MONEY_SETTLEMENT_LIVE\"]\n            and bool(pointer[\"resources\"])\n        ),\n    }\n\nprint(discover_nettrux())\n```\n\nLive output shape (abridged):\n\n```\n{\n  'name': 'Nettrux',\n  'resource_host': 'https://nettrux-edge.fly.dev',\n  'resources_empty': True,\n  'gates': {\n    'EDGE_X402_DISCOVERY_PUBLISHED': False,\n    'X402_REAL_MONEY_SETTLEMENT_LIVE': False,\n    'GUEST_X402_CHECKOUT_ENABLED': False\n  },\n  'openapi_title': 'Nettrux Edge API',\n  'openapi_paths': 126,\n  'commit_documented': True,\n  'ready_for_public_pay_and_book': False\n}\n```\n\nThat last boolean is the whole point of honest discovery. An agent that can parse gates is an agent that will not invent a live rail that is not there.\n\nDiscovery gets you the map. Quoting and booking still need:\n\n`POST /v1/quotes`.\nShippers who just need freight moved today use the portal at [nettrux.ai](https://www.nettrux.ai): LTL, FTL, and air are live; ocean is in build; tracking links and load documents do not require an agent.\n\nNo. The discovery pointer is live. The `resources` array is empty. Discovery publish, real-money settlement, and guest checkout are all false.\n\nSo agents can find the OpenAPI, the freight API host, and the gate flags without guessing. An empty catalog with honest gates beats a marketing page that pretends settlement is open.\n\nOnly with a bearer token Nettrux has already issued to your tenant, and only against the authenticated handshake documented in the OpenAPI. Without a token you get `401 missing_token`. Public guest checkout is flagged off.\n\nNo. Other APIs sell logistics *data* over x402. The claim is about a 3PL building a native x402 transaction surface for the move itself.\n\nQuestions for agent builders: [dispatch@nettrux.net](mailto:dispatch@nettrux.net).\n\n*Alex Carney, Nettrux*", "url": "https://wpnews.pro/news/how-an-ai-agent-discovers-a-3pl-over-x402", "canonical_source": "https://dev.to/nettrux/how-an-ai-agent-discovers-a-3pl-over-x402-3d2c", "published_at": "2026-11-09 22:17:16+00:00", "updated_at": "2026-11-09 22:23:45.921595+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools"], "entities": ["Nettrux", "x402", "Base", "Fly.io"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/how-an-ai-agent-discovers-a-3pl-over-x402", "markdown": "https://wpnews.pro/news/how-an-ai-agent-discovers-a-3pl-over-x402.md", "text": "https://wpnews.pro/news/how-an-ai-agent-discovers-a-3pl-over-x402.txt", "jsonld": "https://wpnews.pro/news/how-an-ai-agent-discovers-a-3pl-over-x402.jsonld"}}