# How an AI agent discovers a 3PL over x402

> Source: <https://dev.to/nettrux/how-an-ai-agent-discovers-a-3pl-over-x402-3d2c>
> Published: 2026-11-09 22:17:16+00:00

Agent builders keep asking the same first question: *where does my agent look?*

Nettrux 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:

`https://www.nettrux.ai/.well-known/x402`
`https://www.nettrux.ai/agents`
`https://nettrux-edge.fly.dev/openapi.json`
This 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.

**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.

**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:

`EDGE_X402_DISCOVERY_PUBLISHED`: false` X402_REAL_MONEY_SETTLEMENT_LIVE`: false` GUEST_X402_CHECKOUT_ENABLED`: false
Nettrux 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.

x402 discovery starts at `/.well-known/x402` on the site an agent already trusts. On Nettrux that document is a **pointer**, not a payable catalog.

``` python
import json
import urllib.request

DISCOVERY = "https://www.nettrux.ai/.well-known/x402"

req = urllib.request.Request(
    DISCOVERY,
    headers={
        "User-Agent": "nettrux-discovery-howto/1.0",
        "Accept": "application/json",
    },
)
with urllib.request.urlopen(req, timeout=20) as r:
    doc = json.load(r)

print(doc["name"], doc["kind"], doc["x402Version"])
print("resourceHost:", doc["resourceHost"])
print("openapi:", doc["openapi"])
print("resources:", doc["resources"])
print("gates:", doc["gates"])
print("payment.claimed:", doc["payment"]["claimed"])
```

Live output from that run:

```
Nettrux resource-server 1
resourceHost: https://nettrux-edge.fly.dev
openapi: https://nettrux-edge.fly.dev/openapi.json
resources: []
gates: {
  "EDGE_X402_DISCOVERY_PUBLISHED": false,
  "GUEST_X402_CHECKOUT_ENABLED": false,
  "X402_REAL_MONEY_SETTLEMENT_LIVE": false
}
payment.claimed: False
```

Read that carefully:

`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.
The 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:

```
for res in doc.get("x402Resources") or []:
    print(res["method"], res["url"])
    print(res["description"][:160], "...")
```

Live output:

```
POST https://nettrux-edge.fly.dev/v1/immediate-bookings/commit
Bearer plus {quote_id}. The OpenAPI summary documents an x402 v1 402 challenge
when X-PAYMENT is absent, and verify, then settle, then book when it is present.
This entry does not name an asset, a chain, or a payee. ...
```

The 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.

``` python
import urllib.error

EDGE_DISCOVERY = "https://nettrux-edge.fly.dev/.well-known/x402"
req = urllib.request.Request(
    EDGE_DISCOVERY,
    headers={"User-Agent": "nettrux-discovery-howto/1.0", "Accept": "application/json"},
)
try:
    urllib.request.urlopen(req, timeout=20)
except urllib.error.HTTPError as e:
    print("Edge discovery HTTP", e.code)
Edge discovery HTTP 404
```

If 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.

The discovery pointer points at the OpenAPI. That document is the request/response contract for every authenticated call. It is public and needs no token.

```
OPENAPI = "https://nettrux-edge.fly.dev/openapi.json"
req = urllib.request.Request(
    OPENAPI,
    headers={"User-Agent": "nettrux-discovery-howto/1.0", "Accept": "application/json"},
)
with urllib.request.urlopen(req, timeout=20) as r:
    oa = json.load(r)

paths = oa["paths"]
print(oa["info"]["title"], "openapi", oa["openapi"], "paths", len(paths))

commit = paths["/v1/immediate-bookings/commit"]["post"]
print("summary:", commit["summary"])
print("security:", commit["security"])
print("responses:", list(commit["responses"]))
body = commit["requestBody"]["content"]["application/json"]["schema"]
print("required body fields:", body["required"])
Nettrux Edge API openapi 3.0.3 paths 126
summary: Commit an immediate booking by PAYING for it over the x402 v1 handshake
(402 challenge without X-PAYMENT; verify → settle → book with it)
security: [{'bearerAuth': []}]
responses: ['200']
required body fields: ['quote_id']
```

A few agent-relevant paths that exist today:

| Path | Methods | Auth | 
|---|---|---|
| `/health` ,`/ready` | GET | none | 
| `/v1/rates/shop` | POST | bearer | 
| `/v1/quotes` | GET, POST | bearer | 
| `/v1/immediate-bookings/commit` | POST | bearer | 
| `/v1/me` | GET | bearer | 
| `/v1/agent/approvals` | GET | bearer | 

The 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.

Unauthenticated health is a useful smoke check that the freight API host is up:

```
HEALTH = "https://nettrux-edge.fly.dev/health"
req = urllib.request.Request(
    HEALTH,
    headers={"User-Agent": "nettrux-discovery-howto/1.0", "Accept": "application/json"},
)
with urllib.request.urlopen(req, timeout=20) as r:
    health = json.load(r)
print(health["ok"], health["data"]["status"])
True healthy
```

Commit without a bearer is rejected. Same day check:

``` python
import urllib.error

url = "https://nettrux-edge.fly.dev/v1/immediate-bookings/commit"
payload = json.dumps({"quote_id": "00000000-0000-4000-8000-000000000000"}).encode()
req = urllib.request.Request(
    url,
    data=payload,
    method="POST",
    headers={
        "Content-Type": "application/json",
        "User-Agent": "nettrux-discovery-howto/1.0",
    },
)
try:
    urllib.request.urlopen(req, timeout=20)
except urllib.error.HTTPError as e:
    print("HTTP", e.code, e.read().decode()[:180])
HTTP 401 {"ok":false,"data":null,"error":{"code":"missing_token",
"message":"Authorization: Bearer <token> is required"}, ...}
```

That is the boundary: discovery is public; booking is authenticated; minting still requires a named human on the Nettrux side of the product.

After the JSON pointer, have the agent (or its author) read the prose contract:

Both 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.

Because `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.

``` python
def discover_nettrux():
    """Public discovery only. Does not quote, book, or pay."""
    import json, urllib.request, urllib.error

    pointer = json.load(urllib.request.urlopen(
        urllib.request.Request(
            "https://www.nettrux.ai/.well-known/x402",
            headers={"Accept": "application/json",
                     "User-Agent": "nettrux-discovery-howto/1.0"},
        )
    ))
    gates = pointer["gates"]
    openapi = json.load(urllib.request.urlopen(
        urllib.request.Request(
            pointer["openapi"],
            headers={"Accept": "application/json",
                     "User-Agent": "nettrux-discovery-howto/1.0"},
        )
    ))

    return {
        "name": pointer["name"],
        "resource_host": pointer["resourceHost"],
        "resources_empty": pointer["resources"] == [],
        "gates": gates,
        "openapi_title": openapi["info"]["title"],
        "openapi_paths": len(openapi["paths"]),
        "commit_documented": "/v1/immediate-bookings/commit" in openapi["paths"],
        "ready_for_public_pay_and_book": (
            gates["EDGE_X402_DISCOVERY_PUBLISHED"]
            and gates["X402_REAL_MONEY_SETTLEMENT_LIVE"]
            and bool(pointer["resources"])
        ),
    }

print(discover_nettrux())
```

Live output shape (abridged):

```
{
  'name': 'Nettrux',
  'resource_host': 'https://nettrux-edge.fly.dev',
  'resources_empty': True,
  'gates': {
    'EDGE_X402_DISCOVERY_PUBLISHED': False,
    'X402_REAL_MONEY_SETTLEMENT_LIVE': False,
    'GUEST_X402_CHECKOUT_ENABLED': False
  },
  'openapi_title': 'Nettrux Edge API',
  'openapi_paths': 126,
  'commit_documented': True,
  'ready_for_public_pay_and_book': False
}
```

That 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.

Discovery gets you the map. Quoting and booking still need:

`POST /v1/quotes`.
Shippers 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.

No. The discovery pointer is live. The `resources` array is empty. Discovery publish, real-money settlement, and guest checkout are all false.

So 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.

Only 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.

No. Other APIs sell logistics *data* over x402. The claim is about a 3PL building a native x402 transaction surface for the move itself.

Questions for agent builders: [dispatch@nettrux.net](mailto:dispatch@nettrux.net).

*Alex Carney, Nettrux*
