Skip to main content
Version: 3.2.0

PAP client walkthrough

This walkthrough uses the AAP 3.2 published PAP 0.3 binding. Frozen AAP 3.1 uses a different October 26 draft. Keep its endpoints and client configuration separate; both upstream profiles say 0.3, so that string alone cannot select the authentication mechanism.

1. Discover the Company and API​

Fetch the dealer's HTTPS /.well-known/poppy.json, verify final-host identity and the supported version. Read domain protected-resource metadata. An explicit 404 or valid empty issuer list permits domain identity; a timeout or failed verification does not. One server selects the issuer; multiple servers require auth.issuer. Verify the issuer's metadata and authorization of the domain. auth.disabled: true keeps issuer identity while disabling new sign-in.

Fetch the advertised OpenAPI and select an implemented operation. Apply bounded HTTPS fetches and DNS/address checks at every hop. Do not forward credentials on redirects. A public MCP link in the AAP bridge is a separate surface; success there does not prove PAP interoperability.

2. Prepare trusted signing code​

Publish CIMD metadata at the exact HTTPS client_id, with same-host HTTPS jwks_uri containing public identity keys. Generate an ES256 delegated key for this User ID at this Company. Have an identity key sign a poppy-delegation+jwt containing only client iss, public cnf.jwk, iat, exp no more than 24 hours later and unique jti. Keep all keys and JWTs outside model context.

Choose an opaque Company-scoped User ID and a fresh unguessable random SID using the 1–256-character base64url ID syntax. Generate at least 128 random bits for unguessability; receivers must not infer entropy from a fixed minimum wire length. Reuse that SID with unchanged User ID and optional Agent ID for the Session. Delegated keys may rotate without changing the SID.

3. Send a fresh Session JWT​

For each API request, sign with the delegated ES256 key. Header: typ: poppy-session+jwt, alg: ES256, kid = delegated-key RFC 7638 thumbprint, poppy_dlg = signed compact delegation. Payload: exact client iss, Company aud, sid, sub, optional agent_id, method htm, target htu without query/fragment, current iat and unique jti.

For signed-out shopping, omit ath and send no OAuth token. There is no guest grant or token-endpoint request:

POST https://demo-toyota.example.com/poppy/skills/inventory.search
Poppy-Session: <fresh-delegated-session-jwt>
Content-Type: application/json

{
"type": "inventory.search.request",
"filters": { "condition": ["used"], "price_max": 25000 },
"pagination": { "limit": 5 },
"sort": { "field": "price", "order": "asc" },
"privacy": { "anonymous": true }
}

The decoded Session and delegation examples have fixed times and invalid illustrative signatures. Generate and sign real requests in code; never copy those fixtures into production.

Read advertised search capabilities before applying filters. Require evidence for equipment, preserve currency/odometer contexts, report unknown data and follow all pagination. A valid HTTP 200 automotive response from this verified PAP operation is evidence of PAP API success. Discovery GETs alone are not.

4. Recover without duplicating actions​

ResultClient action
HTTP 400 invalid_sessionInspect safe error description; verify delegation, audience, fresh time, request binding and unchanged Session identity; send a fresh JWT after correction
HTTP 400 session_endedStart a new SID and retry once; stop if that also ends
OAuth 401/403Handle token/scope challenge separately; Session failures do not invalidate a valid OAuth token
HTTP 429Wait at least Retry-After
Uncertain lead outcomeReconcile using the original stable idempotency key; never create another key or switch bindings to retry blindly

JWT signature verification is separate from customer consent. Submit no dealer lead without explicit approved recipient, payload and contact channels. Test with a non-delivering sink.

5. Use optional features only when implemented​

Account access uses ordinary OAuth. auth.disabled blocks new sign-in; previously issued refresh tokens keep working until revoked. Send a token only with a Session JWT containing its SHA-256 base64url ath; token and JWT belong to the same client. Validate DPoP separately when required. Do not send access tokens for anonymous-browsing User IDs. Scope reduction is allowed; scope expansion is not.

Native PAP MCP sends the Session header and ordinary OAuth when needed, without the old mandatory token exchange. Browser joining authorizes an own-browser code by JSON POST to /authorize with the Session header before the browser redeems it. Never send JWTs on ordinary web pages or authorize a code supplied in a message.

Record safe request IDs, times, route/interface, status and validation stage. Omit JWTs, credentials, keys and private mappings from logs and reports. A live integration still needs independent signer, DNS/TLS, replay/Session-store, consent and actual shopping-agent acceptance; schema checks alone cannot establish those results.