Hilt Pay API

Hilt Pay API turns stablecoin payments into receipts, entitlements, credits, memberships, webhooks, and product state. Use it when you need to protect an API endpoint, AI tool, private dataset, paid agent, bot, software feature, research product, or technical community without manually clicking through the Hilt dashboard. The public API namespace is /v1/access.

Public launch scope

Hilt Pay API is Solana USDC first, with x402 as the protected-resource protocol shape for agent/API flows. Current public scope:
  • solana_usdc is the only production-enabled settlement rail.
  • x402 is the HTTP 402 Payment Required protocol pattern for protected-resource payment requirements.
  • base_usdc and evm_usdc remain future rail work, not a public launch claim.
For developers building today, the path is simple: create an app, create a product, protect your endpoint with POST /v1/access/entitlements/check, return HTTP 402 when Hilt says the customer is unpaid, and let Hilt create the Solana USDC payment session and entitlement record. Future Base and EVM rail work exists behind Hilt production gates. Do not show those rails to buyers until they are returned in payment_session_options.session_creatable_rails[]. USDT is also scaffolded as a future review rail family because it may matter for Asia-heavy merchant demand, but it is not a launch claim. The scaffolded child rails are solana_usdt, erc20_usdt, and tron_usdt; all remain review/research-only with no checkout exposure, no receipt creation, and no entitlement activation.

Rail parity contract

Hilt Pay API uses one production rail parity contract. Rail mechanics can differ, but the Hilt outcome must be identical: payment/session -> proof -> settlement/finality -> receipt -> entitlement -> webhook -> analytics/support/audit -> reconciliation Every live rail must satisfy the same institutional lifecycle:
  1. create a session or payment requirement
  2. normalize proof into a canonical Hilt shape
  3. derive a replay-safe fingerprint
  4. verify payment through the rail-specific verifier or facilitator
  5. prove settlement or finality
  6. create or map a receipt
  7. activate entitlement only after bound receipt/proof evidence
  8. emit webhooks
  9. retain support and audit evidence
  10. support reconciliation, alerts, and kill switch drills
x402 is the HTTP 402 Payment Required protocol for agent/API payments. In Hilt Pay API, an x402 payment requirement includes the settlement network, token, merchant recipient, amount, and resource in paymentRequirements.accepts[]. At public launch, x402 requirements settle over solana_usdc; Base or allowlisted EVM settlement requires a later production rail release. Hilt Pay API is designed to stay zero-custody on every settlement rail. Buyer funds must never route through a Hilt escrow wallet. For Base/EVM-style buyer checkout, the preferred fee model is buyer-one-sign: the buyer pays the merchant wallet directly, Hilt verifies that settlement, and Hilt collects its infrastructure fee from the merchant’s scoped, revocable allowance after the buyer payment is verified. Split buyer transfers remain an advanced fallback for batch-capable wallets, but they are not the default buyer experience. Dynamic checkout must render only payment_session_options.session_creatable_rails[]. Product allowed_rails is policy, not buyer-visible checkout. A rail can be configured and allowed but still hidden because a payout wallet, verification, production gate, registered/gated adapter, or kill switch is missing. Production rail control lives in Hilt platform settings, not scattered .env files. Rail status, provider policy, webhook policy, token policy, finality policy, allowlisted chains, and secret references are managed centrally so agent-created integrations inherit the same reviewed controls as dashboard-created integrations. Every live agent or checkout payment session also writes a Hilt-authored payment requirement record. That requirement binds owner, access product, backing Hilt product, pending entitlement, rail, amount, recipient, fee policy, expiry, and protocol payload before the buyer or agent submits proof. Live non-Solana proofs must bind back to this requirement before receipt mapping or entitlement activation can proceed.

Payment proof and safety model

Every payment rail must follow the same safety path before Hilt grants access:
  1. record an owner-scoped payment proof fingerprint
  2. reject duplicate or replayed proof fingerprints
  3. record verification attempts, including failed attempts
  4. record settlement evidence and finality state
  5. map verified settlement to a Hilt receipt/proof record
  6. evaluate entitlement activation eligibility
This model is rail-abstracted. Recording a proof is not the same thing as accepting a payment or granting access. Current launch-gate status:
RailProof modelProof validation contractLive verificationSettlement/finalityReceipt mappingLive entitlement activation
solana_usdcYeshilt_solana_usdc_payment_v1Current Hilt checkout/payment stateCurrent Hilt payment stateCurrent receipt/member bridgeCurrent verified rail
x402Yesx402_payment_signature_v1HTTP 402 protocol requirement plus facilitator /verify candidate; public launch settlement is Solana USDC onlyLive-candidate /settle evidence exists, not public-liveGated receipt creation/mapping rehearsal onlyBlocked unless explicit live-apply gates are enabled
base_usdcYeserc20_transfer_v1 constrained to Base eip155:8453 and native Base USDCFuture rail candidate onlyRequires provider health, block freshness, finality, and reorg policyGated receipt creation/mapping rehearsal onlyBlocked unless explicit live-apply gates are enabled
evm_usdcYeserc20_transfer_v1 on explicitly allowlisted eip155 chains onlyFuture rail candidate onlyRequires chain allowlist, provider health, provider quorum, block freshness, finality, and reorg policyGated receipt creation/mapping rehearsal onlyBlocked unless explicit live-apply gates are enabled
solana_usdtScaffoldsolana_token_transfer_v1Review-requiredBlockedBlockedBlocked
erc20_usdtScaffolderc20_transfer_v1Review-requiredBlockedBlockedBlocked
tron_usdtScaffoldtron_transfer_v1Research/review-requiredBlockedBlockedBlocked
Public launch remains Solana USDC scoped until every additional rail passes verification, replay protection, settlement/finality, receipt mapping, support evidence, monitoring, and kill-switch gates. A recorded proof, by itself, must never grant access. For x402 flows, Hilt returns a machine-readable HTTP 402 payment requirement. At public launch that requirement settles over Solana USDC. Future Base and EVM settlement can use the same product and entitlement lifecycle once Hilt enables those rails for production. The proof lifecycle is:
  1. proof fingerprint recorded
  2. verification attempt recorded
  3. settlement evidence recorded when verification and finality pass
  4. receipt mapping checked, or gated receipt creation run during controlled rehearsal
  5. entitlement activation checked and blocked unless the explicit live-apply gate, rail allowlist, registry production state, and tuple-binding checks all pass

Hilt Pay vs Hilt Pay API

AreaHilt PayHilt Pay API
Primary jobHosted checkout and merchant workspaceDeveloper and agent infrastructure for paid access
Buyer flowCurrent Hilt checkoutRail-native payment sessions
Developer objectProduct, payment, receipt, membershipApp, access product, payment session, entitlement
Main access pathMerchant dashboard/member recordsPOST /v1/access/entitlements/check
Best forMerchants selling with the Hilt workspaceDevelopers protecting APIs, AI endpoints, tools, datasets, and private products
Hilt Pay API reuses existing Hilt products, Solana USDC settlement, payment state, receipts, memberships, webhooks, support context, SDKs, and API key foundations. It adds an agent-ready payment-to-access surface on top.

What is live now

Represented in the public Hilt Pay API:
  • rail-native apps, products, sessions, entitlements, receipt links, and webhook payload context
  • solana_usdc as the current verified rail through existing Hilt checkout and settlement primitives
  • scoped permissions: access:read, access:write, access:webhooks
  • idempotency keys on write endpoints
  • owner-scoped entitlement checks by product, external customer, wallet, or email
  • payment-session creation for active solana_usdc Hilt Pay API products
  • sandbox payment sessions with deterministic sandbox proof confirmation
  • webhook endpoint creation through the existing Hilt webhook system
  • deterministic rail capability and kill-switch errors
  • staff-only inspection, narrow repair, reconciliation, and metric-spec primitives for controlled operations
Current rail capability posture:
RailV0 statusSandboxLivePublic claim
solana_usdcproduction_availableYesYesCurrent registered/gated adapter through existing Hilt Pay checkout and settlement state
x402sandbox_availableYesGated off by production capabilityHTTP 402 protocol shape for agent/API flows; public launch settlement is Solana USDC only
base_usdcprivate_alpha_targetNoGated off by production capabilityFuture rail candidate; no live receipt or entitlement activation claim
evm_usdcprivate_alpha_targetNoGated off by production capabilityFuture rail candidate with explicit allowlisted-chain posture; no broad EVM acceptance
usdtreview_requiredNoNoConditional/review rail because issuer, network, compliance, support, and dispute risks differ
solana_usdtreview_requiredNoNoFuture USDT scaffold; no checkout, receipt, or entitlement activation
erc20_usdtreview_requiredNoNoFuture USDT scaffold with chain/contract allowlist requirements
tron_usdtreview_requiredNoNoResearch scaffold; TRON requires separate provider, finality, support, and compliance model
The Hilt Pay API service path will not activate a live entitlement unless payment evidence is verified, replay-protected, mapped into Hilt receipt/payment state, and bound to the same owner, product, customer, receipt, payment, and entitlement.

Protect an AI API endpoint in 20 minutes

This is the core developer path: your endpoint asks Hilt whether the customer has access, returns HTTP 402 when unpaid, and retries after Hilt confirms payment and activates the entitlement.

1. Create a scoped server key

Create or request a server-side API key with the narrowest permissions:
PermissionUse
access:readlist rails and check entitlements
access:writecreate apps, products, and payment sessions
access:webhookscreate webhook endpoints
Never put a Hilt key in frontend code.

2. Create an app, configure rails, and create a product

Create an app boundary:
curl https://api.hilt.so/v1/access/apps \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_API_KEY" \
  -H "Idempotency-Key: app-pro-ai-api-001" \
  -d '{
    "name": "Pro AI API",
    "external_app_id": "pro-ai-api",
    "rail": "solana_usdc",
    "default_rail": "solana_usdc"
  }'
Configure the merchant payout setting for each rail before asking an agent to create live payment sessions. In the current public runtime, configure live solana_usdc first:
curl -X PUT https://api.hilt.so/v1/access/rail-settings/solana_usdc \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_API_KEY" \
  -H "Idempotency-Key: rail-settings-solana-usdc-001" \
  -d '{
    "enabled": true,
    "mode": "live",
    "payout_address": "YOUR_SOLANA_PAYOUT_WALLET",
    "label": "Primary Solana USDC payout"
  }'
You may store merchant rail settings for x402, base_usdc, or evm_usdc during gated setup, but they remain hidden from buyer checkout until Hilt enables production verification, replay protection, receipt mapping, entitlement activation, monitoring, and support controls for that rail. For Base/EVM rails with Hilt fees, configure the merchant-side fee collection permission as part of rail setup. The intended production posture is:
  • buyer signs one transfer to the merchant;
  • Hilt verifies the buyer payment;
  • Hilt collects its fee from the merchant’s scoped allowance after verified settlement.
Example Base shape:
curl -X PUT https://api.hilt.so/v1/access/rail-settings/base_usdc \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_API_KEY" \
  -H "Idempotency-Key: rail-settings-base-usdc-001" \
  -d '{
    "enabled": true,
    "mode": "live",
    "payout_address": "YOUR_BASE_USDC_PAYOUT_WALLET",
    "label": "Base USDC payout",
    "fee_collection_mode": "merchant_allowance_pull",
    "fee_collector_address": "HILT_FEE_COLLECTOR_CONTRACT",
    "fee_allowance_token_contract": "BASE_USDC_TOKEN_CONTRACT",
    "fee_allowance_limit_minor_units": 100000000,
    "fee_allowance_tx_hash": "ALLOWANCE_APPROVAL_TX_HASH"
  }'
The setting is still review-gated. A live Base/EVM product with a fee policy should not create buyer sessions until fee_collection.status is active. Create an access product backed by existing Hilt product and payment infrastructure:
curl https://api.hilt.so/v1/access/products \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_API_KEY" \
  -H "Idempotency-Key: product-pro-ai-monthly-001" \
  -d '{
    "access_app_id": "APP_ID",
    "external_product_id": "pro-ai-api",
    "title": "Pro AI API - 30 days",
    "description": "Access to the paid AI endpoint.",
    "amount_minor_units": 25000000,
    "merchant_wallet": "YOUR_SOLANA_PAYOUT_WALLET",
    "entitlement_duration_days": 30,
    "default_rail": "solana_usdc",
    "allowed_rails": ["solana_usdc"],
    "status": "active",
    "live_mode_confirmed": true
  }'
Use status: "paused" while wiring the integration. Active live products require explicit live_mode_confirmed: true. allowed_rails is the product policy, not the checkout menu. Agents and buyers must only show rails returned in payment_session_options.session_creatable_rails[]; future rails that are allowed but not live stay hidden with blocker reason codes. Check setup readiness:
curl "https://api.hilt.so/v1/access/setup/readiness?external_product_id=pro-ai-api" \
  -H "X-Hilt-Key: $HILT_API_KEY"
Inspect the dynamic checkout rail contract:
curl "https://api.hilt.so/v1/access/products/available-rails?external_product_id=pro-ai-api" \
  -H "X-Hilt-Key: $HILT_API_KEY"
For public launch, expect solana_usdc to be the only live session-creatable rail unless Hilt explicitly enables another production rail for your account.

3. Protect the endpoint

import os
import httpx
from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()
HILT_API_URL = os.getenv("HILT_API_URL", "https://api.hilt.so")
HILT_API_KEY = os.environ["HILT_API_KEY"]
PRODUCT = os.getenv("HILT_PAY_API_PRODUCT", "pro-ai-api")
RAIL = os.getenv("HILT_PAY_API_RAIL", "solana_usdc")
PAYMENT_PROTOCOL = os.getenv("HILT_PAY_API_PAYMENT_PROTOCOL", "x402")
SETTLEMENT_RAIL = os.getenv("HILT_PAY_API_SETTLEMENT_RAIL", RAIL)

async def create_payment_requirement(external_customer_id: str):
    async with httpx.AsyncClient(timeout=10) as client:
        response = await client.post(
            f"{HILT_API_URL}/v1/access/payment-sessions",
            headers={"X-Hilt-Key": HILT_API_KEY},
            json={
                "external_product_id": PRODUCT,
                "payment_protocol": PAYMENT_PROTOCOL,
                "settlement_rail": SETTLEMENT_RAIL,
                "external_customer_id": external_customer_id,
                "metadata": {"resource": "https://api.example.com/ai/pro"},
            },
        )
    response.raise_for_status()
    return response.json()

async def require_paid_access(x_customer_id: str = Header(...)):
    async with httpx.AsyncClient(timeout=10) as client:
        response = await client.post(
            f"{HILT_API_URL}/v1/access/entitlements/check",
            headers={"X-Hilt-Key": HILT_API_KEY},
            json={
                "external_product_id": PRODUCT,
                "rail": RAIL,
                "external_customer_id": x_customer_id,
            },
        )
    response.raise_for_status()
    entitlement = response.json()
    if not entitlement["has_access"]:
        payment = await create_payment_requirement(x_customer_id)
        raise HTTPException(status_code=402, detail={
            "code": "payment_required",
            "payment_protocol": PAYMENT_PROTOCOL,
            "settlement_rail": SETTLEMENT_RAIL,
            "entitlement": entitlement,
            "payment_session": payment["payment_session"],
            "payment_requirement": payment["payment_session"].get("payment_requirement"),
        })
    return entitlement

@app.post("/ai/pro")
async def pro_ai_endpoint(entitlement: dict = Depends(require_paid_access)):
    return {
        "answer": "paid response",
        "receipt_id": entitlement.get("receipt_id"),
        "rail_id": entitlement.get("rail_id"),
    }
Minimal Express version:
import express from "express";

const app = express();
app.use(express.json());

const HILT_API_URL = process.env.HILT_API_URL ?? "https://api.hilt.so";
const HILT_API_KEY = process.env.HILT_API_KEY!;
const PRODUCT = process.env.HILT_PAY_API_PRODUCT ?? "pro-ai-api";

async function hilt(path: string, body: unknown) {
  const response = await fetch(`${HILT_API_URL}${path}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Hilt-Key": HILT_API_KEY,
    },
    body: JSON.stringify(body),
  });
  if (!response.ok) throw new Error(await response.text());
  return response.json();
}

app.post("/ai/pro", async (req, res) => {
  const externalCustomerId = req.header("X-Customer-Id");
  if (!externalCustomerId) return res.status(400).json({ error: "X-Customer-Id required" });

  const entitlement = await hilt("/v1/access/entitlements/check", {
    external_product_id: PRODUCT,
    rail: "solana_usdc",
    external_customer_id: externalCustomerId,
  });

  if (!entitlement.has_access) {
    const payment = await hilt("/v1/access/payment-sessions", {
      external_product_id: PRODUCT,
      rail: "solana_usdc",
      payment_protocol: "x402",
      settlement_rail: "solana_usdc",
      external_customer_id: externalCustomerId,
      metadata: { resource: "/ai/pro" },
    });

    return res.status(402).json({
      code: "payment_required",
      payment_protocol: "x402",
      settlement_rail: "solana_usdc",
      entitlement,
      payment_session: payment.payment_session,
      payment_requirement: payment.payment_session?.payment_requirement,
    });
  }

  res.json({
    answer: "paid response",
    receipt_id: entitlement.receipt_id,
    rail_id: entitlement.rail_id,
  });
});

4. Return a payment requirement for denied users

Before creating a session, read GET /v1/access/products/available-rails and render only payment_session_options.session_creatable_rails[] to the buyer or calling agent. At public launch, the live settlement rail is solana_usdc.
curl https://api.hilt.so/v1/access/payment-sessions \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_API_KEY" \
  -H "Idempotency-Key: session-cust-123-pro-ai-001" \
  -d '{
    "external_product_id": "pro-ai-api",
    "external_customer_id": "cust_123",
    "email": "buyer@example.com",
    "rail": "solana_usdc",
    "metadata": {
      "resource": "https://api.example.com/ai/pro"
    }
  }'
The response includes a pending entitlement anchor and a Hilt-authored payment requirement/session. Do not grant access from the pending session. The agent or buyer completes the Solana USDC payment, Hilt verifies finalized settlement, creates a signed receipt, activates entitlement state, and the protected resource is retried after entitlement check returns has_access: true. For x402, Base, or allowlisted EVM settlement, the same conceptual flow is used only after those gates are production-enabled. Until then, they must not be shown as live checkout or access options.

5. Retry after verified payment

After Hilt confirms payment and receipt/member state, call the entitlement check again:
curl https://api.hilt.so/v1/access/entitlements/check \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_API_KEY" \
  -d '{
    "external_product_id": "pro-ai-api",
    "rail": "solana_usdc",
    "external_customer_id": "cust_123"
  }'
Allow response:
{
  "has_access": true,
  "status": "active",
  "product_id": "hilt-product-id",
  "access_product_id": "access-product-id",
  "external_product_id": "pro-ai-api",
  "rail_id": "solana_usdc",
  "external_customer_id": "cust_123",
  "active_from": "2026-05-27T10:00:00Z",
  "expires_at": "2026-06-26T10:00:00Z",
  "receipt_id": "receipt-id",
  "reason": "active_membership",
  "last_payment_id": "payment-id",
  "source": "membership_bridge"
}
Deny response:
{
  "has_access": false,
  "status": "not_found",
  "reason": "no_entitlement",
  "rail_id": "solana_usdc",
  "receipt_id": null,
  "last_payment_id": null,
  "source": "none"
}

6. Run the local demo

The repository includes a FastAPI demo:
cd examples/hilt-access-fastapi
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
copy .env.example .env
uvicorn main:app --reload --port 8088
Try the unpaid request:
curl -i -X POST http://127.0.0.1:8088/ai/pro \
  -H "Content-Type: application/json" \
  -H "X-Customer-Id: cust_123" \
  -d "{\"prompt\":\"summarize this research dataset\"}"
Create a local demo payment session:
curl -X POST http://127.0.0.1:8088/checkout \
  -H "Content-Type: application/json" \
  -d "{\"external_customer_id\":\"cust_123\",\"email\":\"buyer@example.com\"}"
Submit local demo proof:
curl -X POST http://127.0.0.1:8088/payment-proof \
  -H "Content-Type: application/json" \
  -d "{\"external_customer_id\":\"cust_123\",\"payment_session_id\":\"demo_ps_FROM_CHECKOUT\",\"payment_signature\":\"demo-x402-signature\",\"settlement_rail\":\"solana_usdc\"}"
For x402 over Base in the local demo, set HILT_PAY_API_SETTLEMENT_RAIL=base_usdc, then submit proof fields that match the returned requirement:
curl -X POST http://127.0.0.1:8088/payment-proof \
  -H "Content-Type: application/json" \
  -d "{\"external_customer_id\":\"cust_123\",\"payment_session_id\":\"demo_ps_FROM_CHECKOUT\",\"payment_signature\":\"demo-x402-base-signature\",\"settlement_rail\":\"base_usdc\",\"network\":\"eip155:8453\",\"asset\":\"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\",\"pay_to\":\"0x1111111111111111111111111111111111111111\",\"max_amount_required\":\"25000000\"}"
Retry /ai/pro; it now returns the paid response.

Entitlement check API

POST /v1/access/entitlements/check
Request fields:
FieldRequiredNotes
product_id or external_product_idYesProduct is owner-scoped before lookup
railOptionalDefaults to the product rail; include it for deterministic rail checks
external_customer_idOne identity requiredBest server-side customer key
walletOne identity requiredWallet lookup path
emailOne identity requiredEmail lookup path
customer_referenceOptionalCompatibility alias for external customer id
Response fields include:
  • has_access
  • status
  • product_id
  • access_product_id
  • external_product_id
  • rail_id
  • rail
  • external_customer_id
  • wallet
  • email
  • active_from
  • expires_at
  • receipt_id
  • receipt
  • reason
  • last_payment_id
  • source
Common deterministic reasons:
ReasonMeaning
active_entitlementActive Hilt Pay API entitlement
active_membershipExisting Hilt membership grants access
payment_pendingA session exists but confirmed access does not
entitlement_expiredAccess window ended
entitlement_revokedAccess was revoked
no_entitlement_for_railRequested rail does not match product/entitlement rail
no_entitlementNo matching active record

Setup readiness API

Agents can inspect setup state without dashboard clicks:
GET /v1/access/setup/readiness?external_product_id=pro-ai-api
curl "https://api.hilt.so/v1/access/setup/readiness?external_product_id=pro-ai-api&mode=live" \
  -H "X-Hilt-Key: $HILT_API_KEY"
The response is owner-scoped and includes:
  • app readiness
  • product readiness
  • effective rail availability and blockers
  • active webhook coverage
  • deterministic blockers[]
  • exact next_actions[] with API endpoints
Use this before creating live payment sessions. It answers whether the app, product, rail settings, payout wallet, and webhooks are ready, and whether the caller must pass an explicit rail. Abbreviated readiness response:
{
  "ready": false,
  "mode": "live",
  "blockers": [
    {
      "code": "webhook_events_missing",
      "endpoint": "POST /v1/access/webhooks",
      "next_action": "Register a webhook covering payment, receipt, activation, and expiry events."
    }
  ],
  "rails": {
    "allowed_rails": ["solana_usdc"],
    "payment_session_options": {
      "rail_required": false,
      "auto_select_rail": "solana_usdc",
      "session_creatable_rails": [
        {
          "rail_id": "solana_usdc"
        }
      ],
      "session_blocked_rails": [
        {
          "rail_id": "x402",
          "payment_session_blocker": "payment_session_adapter_not_implemented"
        }
      ]
    }
  }
}
You can also inspect rails by external product id:
GET /v1/access/products/available-rails?external_product_id=pro-ai-api
curl "https://api.hilt.so/v1/access/products/available-rails?external_product_id=pro-ai-api&mode=live" \
  -H "X-Hilt-Key: $HILT_API_KEY"
The rail response separates three ideas that agents must not collapse:
  • available_rails[]: rails the merchant has allowed, configured, and passed platform gates for the selected mode
  • blocked_rails[]: rails hidden from checkout because a payout wallet, product allowlist, verification, kill switch, or platform gate is missing
  • payment_session_options.session_creatable_rails[]: rails that can actually create a live Hilt Pay API payment session in this deployment
Dynamic checkout rule: buyer checkout and agent purchase flows should render only payment_session_options.session_creatable_rails[]. Do not render allowed_rails[], available_rails[], or blocked_rails[] directly as buyer choices. Blocked rails stay hidden from checkout and remain available to operators and agents as reason-coded setup context. When creating a product, allowed_rails may be an explicit list or "all" / "all_enabled" for the full known rail set. This still does not expose review rails to buyers. Hilt expands the policy, then filters it through merchant payout settings, production gates, registered/gated adapters, and kill switches before returning session_creatable_rails[]. If more than one session-creatable rail exists, payment_session_options.rail_required is true and the caller must pass rail to POST /v1/access/payment-sessions. If a rail is configured but no registered adapter can create a gated session for it yet, payment_session_options.session_blocked_rails[] marks it with payment_session_adapter_not_implemented. Abbreviated rail availability response:
{
  "mode": "live",
  "default_rail": "solana_usdc",
  "allowed_rails": ["solana_usdc"],
  "available_rails": [
    {
      "rail_id": "solana_usdc",
      "available": true,
      "blockers": [],
      "payment_session": {
        "creatable": true
      }
    }
  ],
  "blocked_rails": [
    {
      "rail_id": "base_usdc",
      "available": false,
      "blockers": ["platform_rail_not_production_enabled"],
      "payment_session": {
        "creatable": false
      }
    }
  ],
  "payment_session_options": {
    "selection_field": "rail",
    "rail_required": false,
    "auto_select_rail": "solana_usdc",
    "session_creatable_rails": [
      {
        "rail_id": "solana_usdc"
      }
    ],
    "session_blocked_rails": [
      {
        "rail_id": "base_usdc",
        "payment_session_blocker": "rail_not_effective"
      }
    ]
  }
}
Configure or update a merchant rail setting with:
PUT /v1/access/rail-settings/{rail_id}
curl -X PUT https://api.hilt.so/v1/access/rail-settings/solana_usdc \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_API_KEY" \
  -H "Idempotency-Key: rail-settings-solana-usdc-001" \
  -d '{
    "enabled": true,
    "mode": "live",
    "payout_address": "YOUR_SOLANA_PAYOUT_WALLET",
    "label": "Primary Solana USDC payout"
  }'
Live PUT /v1/access/rail-settings/x402, PUT /v1/access/rail-settings/base_usdc, PUT /v1/access/rail-settings/evm_usdc, PUT /v1/access/rail-settings/solana_usdt, PUT /v1/access/rail-settings/erc20_usdt, and PUT /v1/access/rail-settings/tron_usdt may store payout settings as pending_review, but they still remain blocked from buyer checkout unless Hilt has explicitly enabled those production rail capabilities. The expected safe result is a reason-coded blocked rail in payment_session_options.session_blocked_rails[], not a buyer-visible checkout option. Base/EVM rail settings also expose fee_collection. Use fee_collection_mode: "merchant_allowance_pull" for buyer-one-sign checkout. The merchant must grant a scoped, revocable fee collector allowance, and Hilt must verify that permission before the rail becomes session-creatable for fee-bearing products. fee_collection_mode: "buyer_split_transfer" is an advanced fallback for wallets that can present batch calls cleanly; it should not be the default human checkout experience.

Common setup blockers

CodeMeaningSafe action
payout_address_requiredThe product allows a rail but no merchant payout wallet/address is configured for that rail.Configure the rail with PUT /v1/access/rail-settings/{rail_id}. Do not create live sessions until readiness is green.
backing_product_wallet_mismatchThe Solana rail setting payout address differs from the legacy backing Hilt Pay product wallet.Stop live session creation for that product, reconcile the intended wallet, and rotate/sync the backing product wallet through support/admin controls.
webhook_events_missingActive webhooks do not subscribe to all recommended lifecycle events.Register a webhook covering payment, receipt, activation, and expiry events.
rail_kill_switchedHilt has disabled a rail through an operational kill switch.Do not retry as live. Wait for Hilt to clear the incident or use another effective rail returned by readiness.
rail_requiredMore than one live rail is effective for the product.Pass rail explicitly to POST /v1/access/payment-sessions.
rail_session_adapter_not_implementedThe rail is configured, but this deployment cannot create live payment sessions for it yet.Do not show the rail in buyer checkout. Use payment_session_options.session_creatable_rails[] until the adapter is live.
merchant_fee_collection_not_activeA Base/EVM fee-bearing product is configured, but merchant-side Hilt fee collection is not active.Complete and verify the merchant allowance/collector setup before creating buyer sessions.
merchant_fee_collection_not_instantA Base/EVM fee-bearing product is configured for deferred merchant billing, which is not allowed for buyer-one-sign payment sessions.Switch the rail setting to merchant_allowance_pull and complete the fee collector approval.
fee_collector_address_requiredA Base/EVM buyer-one-sign fee model is selected, but no Hilt fee collector address is configured.Add the reviewed Hilt fee collector contract/address to the rail setting.
no_available_railsNo product-allowed, merchant-configured, platform-enabled live rail is effective.Inspect blocked_rails[] and fix the first blocker before creating sessions.
no_session_creatable_railsRail settings are effective, but none can create a live payment session in this deployment.Do not create a session. Ask Hilt support or use a session-creatable rail.
Support must not manually activate live entitlements from unverified payment evidence. Sandbox proofs only activate sandbox entitlements.

Billing and rail verification controls

Hilt Pay API has internal/admin billing sync stubs for first-party plan entitlement state:
  • Stripe sync can write a Stripe-backed plan entitlement.
  • Hilt Pay API on-chain sync can record an already verified on-chain source.
  • Both paths are admin-only, idempotent, and replace the current row transactionally.
  • Owner-approved Stripe Checkout for Hilt Pay API subscriptions is exposed separately at POST /v1/access/billing/checkout/stripe and uses admin-configured Price IDs.
  • Admin sync paths do not create Stripe subscriptions, create Hilt Pay API payment sessions, charge customers, or enable automatic recurring on-chain billing.
Rail setting verification is also admin-reviewed. A merchant rail can move from pending_review to verified or rejected, but unsupported rails cannot be verified for live use until platform production gates pass. Live rail verification and Hilt treasury wallet changes require a two-person approval flow in controlled operations:
  • first admin creates an approval request
  • a different admin approves or rejects it
  • rail verification can be applied only after the second approval
  • Hilt treasury wallet approval is recorded only; the V0 stub does not change a live treasury wallet or enable live plan payments
Support list responses include operational affordances for private alpha:
  • approval queue age_seconds and oldest_pending_age_seconds
  • pending rail review pending_review_age_seconds
  • stale billing entitlement warnings[] and stale_warning_count
Stripe billing now has two controlled paths:
  • POST /v1/admin/access/billing/stripe-webhook accepts the raw Stripe webhook body, verifies Stripe-Signature server-side, records replay state by Stripe event id, dead-letters deterministic sync failures, and feeds the billing entitlement writer.
  • The older manual POST /v1/admin/access/billing/stripe-webhook-sync stub is disabled for launch safety. Operators should use the signed webhook intake, recorded event replay, or Stripe API reconciliation instead.
Supported Stripe events are customer.subscription.created, customer.subscription.updated, and customer.subscription.deleted. Unsupported event types are recorded as ignored so Stripe is not retried for irrelevant deliveries. Missing owner metadata such as hilt_owner_id is dead-lettered for support review instead of silently granting access. Support can inspect and recover Stripe webhook state:
  • GET /v1/admin/access/billing/stripe-webhook-events
  • GET /v1/admin/access/billing/stripe-webhook-events/{stripe_event_id}
  • POST /v1/admin/access/billing/stripe-webhook-events/{stripe_event_id}/replay
  • POST /v1/admin/access/billing/stripe-webhook-events/recover-stale
Support can inspect rail proof readiness and proof backbone state:
  • GET /v1/admin/access/rails/production-readiness
  • GET /v1/admin/access/rails/proof-contracts
  • GET /v1/admin/access/rail-proofs
  • GET /v1/admin/access/rail-proofs/{proof_id}
  • GET /v1/admin/access/rail-proofs/{proof_id}/verification-attempts
  • GET /v1/admin/access/rail-proofs/{proof_id}/settlement-evidence
  • GET /v1/admin/access/rail-proofs/{proof_id}/dry-run-apply
These endpoints are staff-only, bounded, redacted, and read-only/dry-run. They do not apply verification, create receipts, enable rails, or activate entitlements. Replay requires an explicit reason and confirm_no_live_payment=true. Direct replay is restricted to dead_letter events. Stale recovery can reprocess old received or processing events after an age threshold, and should normally be run first with dry_run=true. Stale billing entitlement reconciliation is admin-triggered:
  • POST /v1/admin/access/billing/entitlements/reconcile-stale
It expires malformed or elapsed active/past_due billing entitlement rows whose current period is missing or no longer current. Apply mode requires dry_run=false and confirm_expire_stale_billing=true. Past-due rows are support state only and do not grant live Hilt Pay API access. The reconciliation does not create Stripe subscriptions, create Hilt Pay API payment sessions, charge cards, or initiate on-chain payments. Stripe API reconciliation is comparison-first:
  • POST /v1/admin/access/billing/stripe-api/reconcile
It compares local Stripe-backed Hilt Pay API billing entitlements with Stripe subscription state. Dry-run reports mismatched plan, status, or period fields. Applying the reconciliation requires dry_run=false and confirm_no_live_payment=true; it syncs billing entitlement state only and still does not create subscriptions, charge cards, or initiate on-chain payments. Operators can use the API-backed CLI:
set HILT_ADMIN_API_BASE_URL=http://127.0.0.1:8000
set HILT_ADMIN_BEARER_TOKEN=...

python scripts/hilt_pay_api_ops_cli.py list-webhook-events --status dead_letter
python scripts/hilt_pay_api_ops_cli.py inspect-webhook-event evt_123 --include-payload
python scripts/hilt_pay_api_ops_cli.py replay-webhook-event evt_123 --reason "metadata fixed" --confirm-no-live-payment
python scripts/hilt_pay_api_ops_cli.py recover-stale-webhooks --reason "recover stuck processing events"
python scripts/hilt_pay_api_ops_cli.py reconcile-stale-billing
python scripts/hilt_pay_api_ops_cli.py reconcile-stale-billing --apply --confirm-expire-stale-billing
python scripts/hilt_pay_api_ops_cli.py reconcile-stripe-api
python scripts/hilt_pay_api_ops_cli.py reconcile-stripe-api --incident-id INC-123 --operator-reason "billing divergence review"
python scripts/hilt_pay_api_ops_cli.py list-stuck-idempotency-claims --min-age-seconds 900
python scripts/hilt_pay_api_ops_cli.py rail-production-readiness
python scripts/hilt_pay_api_ops_cli.py list-proof-contracts
python scripts/hilt_pay_api_ops_cli.py list-rail-proofs --rail-id base_usdc
python scripts/hilt_pay_api_ops_cli.py inspect-rail-proof PROOF_UUID
python scripts/hilt_pay_api_ops_cli.py list-rail-proof-verification-attempts PROOF_UUID
python scripts/hilt_pay_api_ops_cli.py list-rail-proof-settlement-evidence PROOF_UUID
python scripts/hilt_pay_api_ops_cli.py dry-run-rail-proof-apply PROOF_UUID
See the Hilt Pay API operator runbook for incident scenarios, confirmations, stop conditions, and rollback notes for each command. For scheduler/reporting systems, use dry-run mode first:
set HILT_ADMIN_API_BASE_URL=http://127.0.0.1:8000
set HILT_ADMIN_BEARER_TOKEN=...
python scripts/hilt_pay_api_scheduled_stale_billing_report.py
Apply mode requires HILT_PAY_API_STALE_BILLING_APPLY=1 and HILT_PAY_API_CONFIRM_EXPIRE_STALE_BILLING=1. Hilt treasury wallet changes have a recorded-only writer:
  • request a two-person approval with action hilt_treasury_wallet_change
  • have a different admin approve it
  • call POST /v1/admin/access/treasury-wallets/record-change with confirm_no_live_plan_checkout=true
The writer records the intended wallet change and marks the approval applied for governance purposes. It does not enable live Hilt plan checkout, rotate a treasury wallet, create an on-chain payment session, or move funds. Telemetry is currently emitted through a single structured-log adapter. No production Prometheus, StatsD, or OpenTelemetry sink is selected in V0. The adapter contract is:
record_access_metric(
    name: str,
    value: float = 1,
    labels: dict[str, str] | None = None,
)
The adapter must enforce label allowlists, redact sensitive values before export, support counters and histograms, and attach alert rules for the metrics returned by GET /v1/admin/access/observability/spec. The alert set now includes rail-specific failure classes:
  • proof verification failures by rail
  • settlement failure, dispute, or reorg-risk evidence
  • x402 facilitator verify/settle failures
  • unhealthy EVM provider evidence
Backend-ready alert definitions can be generated from the service-owned spec:
python scripts/generate_hilt_pay_api_alert_rules.py
Generated output:
infra/alerts/hilt_pay_api_private_alpha_alert_rules.json
Optional DogStatsD-compatible export can be enabled without changing call sites:
set HILT_PAY_API_METRICS_BACKEND=statsd
set HILT_PAY_API_STATSD_HOST=127.0.0.1
set HILT_PAY_API_STATSD_PORT=8125
set HILT_PAY_API_STATSD_PREFIX=hilt
Structured logging remains the default. Migration preflight for the Hilt Pay API backbone is read-only:
set HILT_PAY_API_PREFLIGHT_DATABASE_URL=postgresql://...
set HILT_PAY_API_PREFLIGHT_CONFIRM_DISPOSABLE=1
python scripts/hilt_pay_api_migration_preflight.py
Run it only against disposable or staging-shaped databases unless the control room approves a production migration plan. For a disposable migration 053 through 061 rehearsal only:
set HILT_PAY_API_PREFLIGHT_DATABASE_URL=postgresql://localhost/..._disposable
set HILT_PAY_API_PREFLIGHT_CONFIRM_DISPOSABLE=1
set HILT_PAY_API_PREFLIGHT_APPLY_MIGRATIONS=1
python scripts/hilt_pay_api_migration_preflight.py
Never use migration apply mode against production or a production-like DSN.

Payment session API

POST /v1/access/payment-sessions
Write requests require Idempotency-Key. Request:
{
  "external_product_id": "pro-ai-api",
  "external_customer_id": "cust_123",
  "email": "buyer@example.com",
  "rail": "solana_usdc"
}
Response includes:
  • payment_session.id
  • payment_session.status
  • payment_session.rail_id
  • payment_session.payment_protocol, when the session uses a protocol such as x402
  • payment_session.settlement_rail_id, when the protocol wraps an underlying settlement rail
  • payment_session.checkout_url
  • payment_session.payment_requirement for proof-based rails
  • payment_session.adapter
  • pending_entitlement
  • rail
  • payment_session_options
For proof-based rails, the session response does not create a Hilt-hosted checkout URL. It returns a rail-specific payment_requirement:
  • x402 returns payment_protocol: "x402" plus paymentRequirements.accepts[] with network, asset, payTo, maxAmountRequired, resource, and proof_type: "x402_payment_signature_v1". The response also includes the launch settlement path, such as settlement_rail_id: "solana_usdc".
  • solana_usdc is the public launch settlement rail. It can back hosted checkout, wallet-transfer proof paths, and x402 protected-resource requirements when the merchant has a verified Solana USDC payout wallet.
  • base_usdc and evm_usdc are future/gated settlement rails. Their ERC-20 transfer requirement contracts exist for rehearsal and review, but they are not public launch rails until provider governance, receipt mapping, activation, and alert gates pass.
For agent/API payments, prefer the protocol-aware request shape:
{
  "external_product_id": "pro-ai-api",
  "external_customer_id": "cust_123",
  "payment_protocol": "x402",
  "settlement_rail": "solana_usdc",
  "amount_minor_units": 1000,
  "metadata": {
    "resource": "https://api.example.com/ai/pro"
  }
}
The legacy rail: "x402" compatibility shape is still accepted during V0, but agents should send payment_protocol and settlement_rail explicitly. Hilt builds the x402 requirement from the settlement rail’s verified payout address and token/network policy. Use settlement_rail: "solana_usdc" for public launch x402 requirements. Base and EVM x402 settlement stay gated/future until their rail gates are completed. Agents should submit completed rail proofs to:
POST /v1/access/payment-proofs
That endpoint records a replay-protected proof bound to the owner, access product, Hilt backing product, rail, customer identity, amount, network, idempotency key, and the Hilt-authored payment requirement. For live non-Solana proofs, the request must include payment_session_id from the server-created session response, and Hilt compares the proof against the stored payment requirement before recording it. Sandbox/candidate proofs may be recorded with livemode: false, but they are not accepted payments. The endpoint does not grant access by itself. Verification, settlement/finality, receipt mapping, and entitlement activation remain separate rail-gated stages. For x402 proofs, the proof payload must include the PAYMENT-SIGNATURE value plus the exact requirement fields Hilt returned: payment_requirements_hash, network, asset, pay_to, max_amount_required, and resource. Hilt stores a proof-to-requirement binding with the proof, including the payment session, pending entitlement, protocol, settlement rail, requirement hash, payment_requirements_hash, and resource. The facilitator /verify and /settle candidate path must match that same stored proof and requirement binding before Hilt records verified evidence. For Base settlement, those fields must match Base eip155:8453, native Base USDC, the merchant payout address, and the exact required amount. A Base transaction hash alone is not enough to satisfy x402; it must be tied back to the HTTP 402 payment requirement. Only settlement rails with production_enabled: true, merchant-approved rail settings, a payout wallet, and a registered payment-session adapter can create live payment sessions. The launch runtime is Solana USDC first. x402 is modeled as a protected-resource payment protocol over eligible settlement paths, starting with solana_usdc. Public x402 receipt mapping and live entitlement activation remain blocked until the x402 production evidence gates pass. base_usdc and evm_usdc have live-shaped adapters, but remain hidden from buyer checkout until their full production gates pass. A rail adapter is not just a label. Before a rail can appear in payment_session_options.session_creatable_rails[], Hilt must register an adapter contract with:
  • session strategy
  • session creation implementation
  • settlement/finality verification status
  • replay-protection policy
  • receipt mapping status
  • entitlement activation evidence
  • support/audit evidence payload
Rails with sandbox_enabled: true but production_enabled: false, such as x402, may be shown in local/developer-demo flows but cannot activate live entitlements. Rails that are configured by a merchant but fail production capability, payout, verification, or kill-switch checks return reason-coded blockers and must not be shown in buyer checkout. x402 has an agent-readable adapter contract and a live-shaped payment requirement adapter in V0, but public production settlement and access activation remain blocked:
  • session_strategy: "direct_http_402"
  • payment_protocol: "x402"
  • settlement_rail_id: "derived_from_payment_requirements" at contract level, then concrete solana_usdc in the public launch session response; base_usdc and evm_usdc remain gated/future settlement values
  • settlement_verification_status: "sandbox_shape_only"
  • receipt creation/mapping is available only through staff-gated non-Solana receipt creation rehearsal or approved production rail launch
  • controlled facilitator verify/settle candidate exists behind service/operator boundaries only
  • live payment-header replay protection and proof-to-requirement binding now exist in the service boundary, but production facilitator/provider evidence and launch drills remain public-launch gates
  • no live entitlement activation from x402 evidence
This lets agents generate and understand the HTTP 402 shape without Hilt claiming live x402 settlement before the production adapter is verified. base_usdc and evm_usdc also expose adapter contracts and live-shaped payment requirement adapters:
  • base_usdc uses session_strategy: "wallet_transfer_proof" and requires Base chain id, USDC token contract, merchant recipient, amount, transaction hash/log uniqueness, healthy provider evidence, finality, and reorg handling before live use.
  • evm_usdc uses session_strategy: "wallet_transfer_proof_allowlisted_chain" and requires explicit chain allowlists, provider policy, ERC-20 transfer parsing, transaction hash plus log-index uniqueness, healthy provider evidence, finality/reorg policy, and per-chain kill switches before live use.
  • For non-custodial fee collection, Base/EVM payment requirements expose required_transfers[], gross_amount_minor_units, merchant_amount_minor_units, hilt_fee_minor_units, fee_collection, and wallet_batching. In the default merchant-allowance model, required_transfers[] contains only the buyer-to-merchant transfer and buyer_signature_requirement is single_transfer_to_merchant.
  • fee_collection.model: "merchant_allowance_pull" means the merchant has configured a scoped Hilt fee collector permission. Hilt may collect only its fee from the merchant-side allowance after the buyer payment is verified. The buyer does not sign the Hilt fee leg, and Hilt does not custody buyer or merchant funds.
  • Split buyer transfers remain available only when explicitly configured as fee_collection_mode: "buyer_split_transfer". In that fallback mode, wallet_batching.wallet_send_calls can include an EIP-5792-style wallet_sendCalls request template with one ERC-20 transfer call to the merchant payout wallet and one ERC-20 transfer call to the Hilt fee wallet. Hilt still treats those as direct non-custodial transfers, not escrow or custody.
  • both return implemented: true because the session requirement adapter exists, but creatable: false until production capability, merchant settings, payout wallet, and kill-switch gates pass.
  • neither rail should be shown as a buyer checkout option from live payment_session_options.session_creatable_rails[] until its production rail gate passes.

Alchemy EVM webhook intake

For Base/EVM rails, Alchemy custom webhooks should be treated as a wake-up signal, not as proof of payment by themselves. Use a custom webhook for ERC-20 Transfer logs and point it at:
POST /v1/access/provider-webhooks/alchemy/evm
Production deployments must set HILT_PAY_API_ALCHEMY_WEBHOOK_SIGNING_KEY and verify the X-Alchemy-Signature header against the raw request body. The endpoint normalizes Alchemy GraphQL webhook payloads into transfer-log candidates, but it does not accept a payment, create a receipt, activate an entitlement, or enable a rail. Hilt must still fetch the transaction receipt, match it against the stored payment requirement, verify finality, then map receipt and access state through the rail-gated flow.

Sandbox contract

Hilt Pay API V0 includes an API-level sandbox contract for agents and developers. It is not live money, not settlement, and not a receipt. Create a sandbox payment session:
curl https://api.hilt.so/v1/access/sandbox/payment-sessions \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_SANDBOX_API_KEY" \
  -H "Idempotency-Key: sandbox-session-cust-123-001" \
  -d '{
    "external_product_id": "pro-ai-api",
    "external_customer_id": "cust_123",
    "email": "buyer@example.com",
    "rail": "x402",
    "confirm_sandbox_mode": true
  }'
The API key must be a sandbox key or a dashboard-authenticated session. A live API key cannot create sandbox sessions unless it is explicitly marked sandbox-capable. The response includes:
  • sandbox_session.id
  • sandbox_session.status: "pending"
  • sandbox_session.proof_type: "hilt_pay_api_sandbox_v1"
  • sandbox_session.proof
  • pending_entitlement.status: "pending"
  • livemode: false
  • sandbox_only: true
  • rail
  • payment_session_adapter
The raw sandbox proof is returned once. Hilt stores a hash of the proof, not the raw proof. Confirm the sandbox proof:
curl https://api.hilt.so/v1/access/sandbox/payment-sessions/SANDBOX_SESSION_ID/confirm \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_SANDBOX_API_KEY" \
  -H "Idempotency-Key: sandbox-confirm-cust-123-001" \
  -d '{
    "proof": "hilt_sandbox_v1...."
  }'
Confirmation activates only the sandbox entitlement created for that owner/session/proof. It does not create a Hilt receipt, does not confirm a live payment, does not update production settlement state, and cannot be promoted into live membership state by support repair. Sandbox failure modes are deterministic:
CodeMeaning
sandbox_key_requiredThe caller is using a live API key instead of a sandbox key/dashboard session
sandbox_mode_confirmation_requiredconfirm_sandbox_mode: true was not supplied
rail_disabledThe requested rail does not have sandbox_enabled: true or is kill-switched
customer_identity_requiredNo external customer id, wallet, or email was supplied
sandbox_session_not_foundThe session does not exist for this owner
invalid_sandbox_proofThe proof hash does not match the session
sandbox_proof_expiredThe proof expired before confirmation
Use sandbox sessions to test denied, pending, allowed, rail-disabled, and owner-isolated behavior before any live payment flow.

Support and reconciliation

Hilt Pay API V0 has staff-only support primitives under /v1/admin/access. They are intentionally narrow and excluded from the public API schema. Support can:
  • list and inspect Hilt Pay API entitlements
  • inspect related Hilt product, payment, membership, sandbox session, webhook delivery, and recent audit context
  • mark pending or failed entitlements as failed
  • revoke active or pending entitlements
  • sync a non-sandbox pending entitlement from an existing active Hilt membership
  • run dry-run or scoped reconciliation for pending non-sandbox entitlements
  • read the controlled observability metric spec
Support cannot:
  • create receipts
  • confirm unverified payments
  • enable disabled rails
  • move money
  • promote sandbox entitlements into live membership state
Reconciliation currently targets three controlled failure modes:
  • payment or membership is active but the Hilt Pay API entitlement is still pending
  • receipt rail link exists but no entitlement is linked
  • Hilt Pay API webhook deliveries are dead-lettered after activation
The reconciliation endpoint records durable run summaries in access_reconciliation_runs. Use dry-run first; non-dry-run repair only activates from verified active membership state.

Webhook setup

curl https://api.hilt.so/v1/access/webhooks \
  -H "Content-Type: application/json" \
  -H "X-Hilt-Key: $HILT_API_KEY" \
  -H "Idempotency-Key: webhook-pro-ai-api-001" \
  -d '{
    "label": "Pro AI API access sync",
    "url": "https://api.example.com/hilt/webhook",
    "subscribed_events": [
      "payment.confirmed",
      "receipt.created",
      "membership.activated",
      "membership.expired",
      "delivery.failed"
    ],
    "product_ids": ["ACCESS_PRODUCT_OR_HILT_PRODUCT_ID"]
  }'
Store the returned signing secret immediately. For Hilt Pay API-backed products, webhook payloads include rail context:
{
  "type": "payment.confirmed",
  "data": {
    "access": {
      "access_product": {
        "id": "access-product-id",
        "external_product_id": "pro-ai-api"
      },
      "rail_id": "solana_usdc",
      "rail": {
        "id": "solana_usdc",
        "rail_id": "solana_usdc",
        "status": "production_available",
        "network": "solana:mainnet",
        "settlement_asset": "USDC",
        "supports_sandbox": true,
        "supports_live": true,
        "requires_review": false,
        "production_enabled": true,
        "sandbox_enabled": true,
        "settlement_verification_status": "verified_current",
        "entitlement_activation_support": "production_after_verified_payment_receipt_or_membership_state",
        "kill_switch_state": "not_tripped"
      }
    }
  }
}
Do not grant access from unsigned webhooks. The entitlement check remains the source of truth for serving protected API responses.

Rail availability

Rail is a first-class field in Hilt Pay API from V0:
  • payment sessions carry rail / rail_id
  • entitlement records carry rail_id
  • receipt links carry rail context
  • webhook payloads include rail context for Hilt Pay API-backed products
  • SDK, Postman, docs, and the demo keep rail explicit
Use:
GET /v1/access/rails
to inspect the current rail capability model. Agents should read this before creating products or sessions. Example rail shape:
{
  "rail_id": "x402",
  "status": "sandbox_available",
  "supports_sandbox": true,
  "supports_live": false,
  "requires_review": true,
  "production_enabled": false,
  "sandbox_enabled": true,
  "confirmation_policy": "Developer-demo shape only; no Hilt live settlement confirmation in V0.",
  "replay_protection_policy": "Required before live use: facilitator proof uniqueness, nonce/payment header replay guard, and receipt proof mapping.",
  "settlement_verification_status": "sandbox_shape_only",
  "entitlement_activation_support": "sandbox_or_manually_seeded_checks_only_no_live_activation",
  "kill_switch_state": "not_tripped"
}
production_enabled gates live payment sessions. sandbox_enabled only means a developer-demo or local test shape exists. It does not mean Hilt will grant live access from that rail.

Agent setup guide

Agent bootstrap V1 lets an AI agent or developer tool start from the API instead of waiting for a dashboard-created key.
POST /v1/access/agent-bootstrap
The bootstrap response returns a sandbox setup intent, a one-time setup_token, a one-time sandbox API key, and an owner approval URL. The sandbox key can create apps, products, webhooks, sandbox sessions, sandbox proofs, and entitlement checks. It cannot create live money movement.
{
  "agent_name": "Acme API Builder",
  "agent_platform": "cursor",
  "requested_use_case": "Protect an AI API endpoint with Hilt Pay API",
  "requested_permissions": ["access:read", "access:write", "access:webhooks"],
  "ttl_hours": 24
}
The owner can approve the intent from a first-party dashboard session:
POST /v1/access/agent-bootstrap/{setup_intent_id}/approve
Agents can poll setup status while waiting for owner approval:
POST /v1/access/agent-bootstrap/{setup_intent_id}/status
Agents can also attach a setup manifest:
POST /v1/access/agent-bootstrap/{setup_intent_id}/manifest
{
  "setup_token": "hst_...",
  "manifest": {
    "app": { "name": "Acme AI", "external_app_id": "acme-ai" },
    "product": {
      "external_product_id": "pro-api",
      "title": "Pro API access",
      "amount_minor_units": 79000000,
      "default_rail": "solana_usdc",
      "allowed_rails": ["solana_usdc"]
    },
    "payment_protocol": "x402",
    "settlement_rail": "solana_usdc",
    "protected_resource": {
      "url": "https://api.acme.test/ai/pro",
      "method": "POST",
      "customer_identity": "external_customer_id"
    },
    "webhook": {
      "url": "https://api.acme.test/hilt/webhook",
      "subscribed_events": ["payment.confirmed", "membership.activated"]
    }
  }
}
Hilt stores the manifest against the setup intent and returns sandbox readiness, live blockers, warnings, normalized fields, and the next API calls the agent should make. This is deliberately not a live apply endpoint. It is the agent-readable planning contract before owner approval. Approval can issue a live API key for the owner’s workspace, but it does not bypass billing, payout wallet, rail health, live-mode confirmation, or production rail gates. This is the core agent-first safety model: agents can build the integration, owners approve live control. Minimum agent setup sequence:
  1. Bootstrap a sandbox setup intent with POST /v1/access/agent-bootstrap, or use an owner-created key when the merchant has already approved the agent.
  2. Create an app with POST /v1/access/apps.
  3. Configure merchant rail settings with PUT /v1/access/rail-settings/{rail_id} for the payout rails the merchant wants Hilt to evaluate.
  4. Create a product with POST /v1/access/products, setting default_rail and allowed_rails.
  5. Check GET /v1/access/setup/readiness and complete the returned next_actions[].
  6. Inspect GET /v1/access/products/available-rails and render only payment_session_options.session_creatable_rails[] in checkout.
  7. Create a session with POST /v1/access/payment-sessions; include rail whenever payment_session_options.rail_required is true.
  8. Check access with POST /v1/access/entitlements/check before serving paid work.
Agents should:
  1. use server-side scoped credentials only
  2. include Idempotency-Key on every write
  3. create paused products by default unless the merchant explicitly confirms live mode
  4. keep allowed_rails, default_rail, and selected rail explicit in product, session, entitlement, webhook, and support state
  5. hide blocked rails from buyer checkout while preserving blocker codes in logs and operator UI
  6. deny protected work unless has_access: true
  7. test the denied path, pending path, active path, sandbox rail path, disabled rail path, rail-required path, and webhook signature path
  8. store Hilt ids and external ids in the app database
Agents must not:
  • embed Hilt keys in client code
  • treat a sandbox bootstrap key as a live key
  • trust client-supplied owner, merchant, product, or entitlement ids without Hilt validation
  • grant access from a pending payment session
  • treat allowed_rails[] as buyer-visible checkout rails
  • treat base_usdc, evm_usdc, usdt, solana_usdt, erc20_usdt, or tron_usdt as live rails without production_enabled: true
  • make live product changes without explicit merchant confirmation

Production-readiness notes

Hilt Pay API is live for the Solana USDC launch scope. Broader enterprise and additional-rail production readiness still requires:
  • repeated staging migration rehearsal and rollback/runbook sign-off
  • database-backed integration tests for idempotency races, live apply, billing evidence, revocation, audit, and rollback on disposable/staging-shaped targets
  • production gate and kill-switch admin controls for each rail
  • admin/support UI and playbook sign-off for pending entitlements, reconciliation, and webhook dead letters
  • KMS/encrypted webhook secret storage or documented risk acceptance
  • production metrics and alerts wired for entitlement latency, pending entitlement age, webhook dead letters, disabled rail attempts, idempotency conflicts, owner-isolation failures, rail proof failures, settlement/reorg risk, x402 facilitator failures, and EVM provider health
  • legal/compliance review of public positioning
Hilt is not a custodian, merchant of record, bank, exchange, broker, or tax advisor. Receipts and proofs should remain factual and source-backed.

Pricing and packaging assumptions

Hilt Pay can stay simple for merchants using the workspace. Hilt Pay API has separate developer/agent pricing from Hilt Pay Workspace:
Hilt Pay API tierMonthlyYearlyLaunch settlement fee
Sandbox$0$0No live money
Starter$79$7901.00% Solana USDC
Growth$349$3,4900.70% Solana USDC
Scale$1,250$12,5000.45% Solana USDC
EnterpriseCustomCustomCustom
At launch, x402 is the payment protocol for agent/API flows over the solana_usdc settlement path. The Stripe subscription Price IDs for Hilt Pay API are runtime settings in admin:
  • stripe_price_hilt_pay_api_starter_monthly
  • stripe_price_hilt_pay_api_starter_yearly
  • stripe_price_hilt_pay_api_growth_monthly
  • stripe_price_hilt_pay_api_growth_yearly
  • stripe_price_hilt_pay_api_scale_monthly
  • stripe_price_hilt_pay_api_scale_yearly
Owners create Hilt Pay API card subscriptions through:
POST /v1/access/billing/checkout/stripe
This endpoint requires a first-party dashboard session. API keys and setup-intent sandbox keys cannot create or change account billing. The Stripe Checkout session includes hilt_product=hilt_pay_api, hilt_owner_id, hilt_pay_api_plan, and hilt_interval metadata so the signed Hilt Pay API Stripe webhook can sync billing entitlement state back into Hilt. Commercial and usage defaults are also runtime settings in admin:
  • hilt_pay_api_plan_sandbox_config
  • hilt_pay_api_plan_starter_config
  • hilt_pay_api_plan_growth_config
  • hilt_pay_api_plan_scale_config
  • hilt_pay_api_plan_enterprise_config
Hilt Pay API packages should account for:
  • access apps
  • access products
  • entitlement check volume
  • webhook delivery volume
  • audit/support retention
  • enabled rail adapters and sandbox/gated capability controls
  • rail-specific verification, finality, gas, facilitator, support, and dispute cost differences
Sandbox, review-required, gated target, disabled, and kill-switched rails should not be sold as live payment rails.