Integration brief for coding agents
A filled copy for one product is GET {facilitator}/v1/adopt/{slug}/agent-brief.md (product document: GET {facilitator}/v1/products/{slug}). Use that when the publisher has a slug. The placeholders below are only for a hand paste before those URLs exist for the product.
Paste this entire document into Cursor / Replit / Claude / Codex. Replace placeholders before or after paste. Staging UI may fill them later.
{{FACILITATOR_URL}} e.g. https://txpass.app
{{PRODUCT_SLUG}} e.g. imprintai-api
{{TXPASS_ISSUER}} e.g. https://txpass.app; set explicitly, exact JWT iss, no trailing slash
{{CORS_ORIGINS}} exact Origins, comma-separated, no trailing slash
Role
You are integrating optional Txpass beside an existing API-key auth system for product {{PRODUCT_SLUG}}. Dual-rail only: do not replace keys, redesign billing, or cut over to Txpass-only.
Warning — second ignition
Enabling Txpass adds a second authenticated path beside API keys (dual-rail). That is intentional: wallet buyers can reach entitled routes without a publisher API key. Production default is on_new_identity=reject unless the publisher explicitly opts into free auto_enroll. Harden with plan allowlists, entitled routes, CORS, and quotas. Developer FAQ: docs/website-faq.md.
Locked rules (v1 — do not violate)
- Dual-rail: accept existing API key or
Authorization: Bearer <txpass-session-jwt>on entitled routes. - Do not replace API keys; keys stay the primary path for non-wallet buyers.
- No human apply on the Txpass path (
skips_human_apply: true) — live entitlement is enough. - Do not mint long-lived API keys from Txpass sessions (session/credential table only; hash at rest).
- Never log raw JWT /
Authorizationheaders. - BYOW: buyer uses their wallet; publisher never receives a private key / WIF.
- Map plan → local tier/quotas locally — never trust client-supplied quotas.
- Plan-dependent
receipt_txid(do not mix):- free →
^autoenroll:[0-9a-f]{32}$only - paid →
^[0-9a-f]{64}$only
- free →
Fetch first (facilitator discovery)
curl -sS {{FACILITATOR_URL}}/v1/protocol
curl -sS {{FACILITATOR_URL}}/v1/products
curl -sS {{FACILITATOR_URL}}/v1/session/jwks
Use /v1/protocol for frozen IDs, the delegation aud and policy hints (newIdentity), /v1/products for each product's routes and new-identity policy, JWKS for verify. (/health is a liveness probe only: { "ok": true, "version": … }.)
Enablement checklist (publisher side)
- Env: set
TXPASS_FACILITATOR_URL={{FACILITATOR_URL}}andTXPASS_ISSUER={{TXPASS_ISSUER}}explicitly.TXPASS_ISSUERmust equal the publisher's expected JWTissexactly, with no trailing slash; production ishttps://txpass.app. - Dual-rail middleware: API key or Bearer Txpass session JWT.
- Verify via JWKS (
GET {{FACILITATOR_URL}}/v1/session/jwks) or@txpass/verify(verifySessionJwt). - Plan allowlist includes
freeif facilitatorauto_enrollis on. - Plan-dependent
receipt_txidvalidation (free vs paid — see Locked rules). - Map plan → local tier/quotas locally.
- No human apply on Txpass path.
- Do not mint long-lived keys from Txpass.
- Never log raw JWT / Authorization.
- Buyer UI: AuthFetch → facilitator
POST /v1/sessionwith{ "product": "{{PRODUCT_SLUG}}" }, then Bearer to publisher API. - Structured verify failure codes in staging (issuer / audience / plan / exp).
- Regression: wrong/expired/forged JWT rejected; API-key path unchanged.
Env vars (publisher)
TXPASS_FACILITATOR_URL={{FACILITATOR_URL}}
TXPASS_ISSUER={{TXPASS_ISSUER}}
# Audience / product claim:
# {{PRODUCT_SLUG}}
# Facilitator (if you operate it): TXPASS_CORS_ORIGINS={{CORS_ORIGINS}}
Verify sketch
import { verifySessionJwt } from '@txpass/verify'
const jwks = await fetch(`${process.env.TXPASS_FACILITATOR_URL}/v1/session/jwks`).then(r => r.json())
const result = await verifySessionJwt(bearer, jwks, {
audience: '{{PRODUCT_SLUG}}',
issuer: process.env.TXPASS_ISSUER,
planAllowlist: ['free', 'pro-month', 'pro-year', 'enterprise-month'], // edit to yours
})
if (!result.ok) return res.status(401).json({ error: 'invalid_txpass_session' })
const tier = mapPlanToTier(result.claims.plan) // local map only
Acceptance tests
- ☐ Facilitator
GET /health,/v1/protocol,/v1/session/jwksOK - ☐ Browser AuthFetch session mint (CORS Origin exact match:
{{CORS_ORIGINS}}) - ☐ Free auto-enroll (if enabled):
plan: free,receipt_txid=autoenroll:+ 32 hex - ☐ Entitled write succeeds with Bearer JWT (no human apply)
- ☐ Paid path expects 64-hex
receipt_txidonly - ☐ API-key path unchanged
- ☐ Wrong / expired / forged JWT rejected
- ☐ Entitlements survive facilitator restart when Postgres /
DATABASE_URLis set
If blocked — ask the human
Do not invent these. Ask for:
- Product slug (
{{PRODUCT_SLUG}}) - CORS origins (
{{CORS_ORIGINS}}) - Issuer string (
{{TXPASS_ISSUER}}— decode a real JWTiss) - Facilitator URL (
{{FACILITATOR_URL}}) - Which routes are entitled vs public-keep-open
- Plan → local tier map
Docs (fetch if needed)
docs/publisher-onboarding.md— intake + full enablementdocs/adopter-quickstart.md— thin pathdocs/facilitator-api.md— HTTP contractdocs/publisher-guide.md— deeper dual-rail notesdocs/website-faq.md— dual-rail / second-ignition FAQ for launch
Humans should not retype protocol details; fetch live endpoints and these docs.