Payment Rails
Keel’s managed payment boundary is payment.execute for Stripe MPP and x402.
It evaluates the bound spend request before dispatch and records the outcome
it observes. An authorization decision alone does not prove receipt, settlement,
or an external state change.
Current boundaries
| Rail | Current Keel contract |
|---|---|
| Stripe MPP | Managed payment decisions and dispatch through the documented payment path. |
| x402 | Managed payment decisions and dispatch through the documented payment path. |
| AP2, PayPal, Visa, Mastercard, OpenAI ACP, UCP | Evidence binding only. Keel does not execute or positively authorize these rails; current payment decisions deny. |
| L402 | Evidence binding only. Keel does not execute Lightning payments; current L402 payment decisions deny. |
| Amex ACE | Not released; no supported settlement path. |
Evidence binding means included rail artifacts can be covered by a signed request binding. It does not mean Keel verified the external effect, granted spend authority, or intercepted a payment your application made directly. Do not treat a signed denied Permit as payment authorization.
Plans and setup
Stripe MPP and x402 are available on every plan. Starter uses the AI Permit-to-Pay template; custom payment authoring needs Production or Enterprise. Configure legitimate payment credentials and follow the relevant rail guide. Both rails require enabled owner-managed project MPP configuration, a trusted customer gateway with an unbound Client key, and operator-enrolled rail hosts. Configuration-state validation alone does not prove merchant or wallet eligibility. Requests beyond the plan allowance are refused; there is no overage.
Configure the trusted payment gateway
Keep ready-to-spend payment material in a trusted customer gateway, outside the
agent process. Use a server-only Client key that is not bound to an AgentPrincipal.
Create it with the project admin key, omitting agent_principal_id. It differs
from the bound key required for managed MCP. An AgentPrincipal-bound key carrying
an SPT or x402 payment payload is refused before policy or dispatch with
403 payment_credential.agent_delivery_forbidden, including expired or revoked
agent bindings. Do not remove an agent binding or deliver this gateway key or
payment credential to an agent. Keel governs requests through its managed
boundary; it does not control direct payments made by the credential-holding
customer gateway. Constrain the agent credentials and network access accordingly.
The project owner must enable the shared payment configuration for both Stripe MPP and x402. These public routes require the authenticated owner user JWT, not a project key:
POST /v1/projects/{project_id}/mpp-config: setstripe_profile_idandenabled: true.GET /v1/projects/{project_id}/mpp-config: readconfigured,enabled, and the saved profile identifier.POST /v1/projects/{project_id}/mpp-config/test: validate enabled configuration state.
The /test probe does not verify merchant eligibility, wallet funding, approval,
or settlement. The shared field is named stripe_profile_id for both rails.
It is configuration/attestation metadata: x402 does not require a Stripe Link account
just to satisfy this field. Supply the actual stable identifier for your gateway
payment profile. Saving an example string does not establish merchant, wallet,
or credential eligibility. Missing
or disabled configuration returns 409 MPP_CONFIG_NOT_ENABLED.
The Keel operator must enroll the exact merchant/resource HTTPS hostname in
mpp_allowed_rail_hosts, the operator-controlled outbound allowlist. Confirm
enrollment before sending payment material. Customer policies and the config
probe cannot enroll arbitrary merchant hosts.
Configure KEEL_OWNER_JWT from the legitimate owner dashboard session,
KEEL_DASHBOARD_ORIGIN to its allowed origin, KEEL_ADMIN_KEY to the server-held
project admin key, KEEL_PROJECT_ID, and STRIPE_PROFILE_ID. Never give these
management credentials to the agent. This Python 3 setup uses the standard library:
import json
import os
import secrets
import time
from urllib.request import Request, urlopen
base_url = os.environ.get("KEEL_BASE_URL", "https://api.keelapi.com").rstrip("/")
project_id = os.environ["KEEL_PROJECT_ID"]
def setup_request(method, path, key, body=None):
headers = {
"Authorization": "Bearer " + key,
"Content-Type": "application/json",
"Origin": os.environ["KEEL_DASHBOARD_ORIGIN"],
"X-Keel-Timestamp": str(int(time.time())),
"X-Keel-Nonce": secrets.token_hex(16),
}
request = Request(
base_url + path,
data=None if body is None else json.dumps(body).encode("utf-8"),
headers=headers, method=method,
)
with urlopen(request, timeout=30) as response:
return json.load(response)
config_path = "/v1/projects/" + project_id + "/mpp-config"
setup_request("POST", config_path, os.environ["KEEL_OWNER_JWT"], {
"stripe_profile_id": os.environ["STRIPE_PROFILE_ID"], "enabled": True,
})
state = setup_request("GET", config_path, os.environ["KEEL_OWNER_JWT"])
if not state["configured"] or not state["enabled"]:
raise RuntimeError("Project payment configuration is not enabled")
setup_request("POST", config_path + "/test", os.environ["KEEL_OWNER_JWT"], {})
credential = setup_request("POST", "/v1/api-keys", os.environ["KEEL_ADMIN_KEY"], {
"scope": "client", "name": "Trusted payment gateway",
})
gateway_key = credential["raw_key"]
# Use only in this gateway; do not print or deliver this secret to an agent.
os.environ["KEEL_API_KEY"] = gateway_keyThis configures the SDK in the same gateway process. Persist gateway_key in
your server-side secret store for later runs, then load it as KEEL_API_KEY.
The raw key is returned only once. SDK calls add freshness headers; raw HTTP
execution must supply a fresh timestamp and nonce on every attempt too. See
Request Freshness.
Independent verification
Use the exact released verifier and the public trust material described in Running Keel Verify. A signed request binding proves what Keel recorded. Provider receipt, provider execution, and external state change each require their own evidence. The binding version in the artifact selects its canonicalization; historical artifacts remain unchanged.