Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell/custom-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,5 @@ vulnz
worktree
yaml
yml
jwks
keyid
4 changes: 2 additions & 2 deletions docs/assets/partner/endorsed/Affirm.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions docs/specification/cart.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ consistent data structures when converting a cart to a checkout session.

{{ schema_fields('context', 'checkout') }}

### Signals

Transaction environment data provided by the platform to support authorization
and abuse prevention. Signal values MUST NOT be buyer-asserted claims. See
[Signals](overview.md#signals) for details and privacy
requirements.

{{ schema_fields('types/signals', 'checkout') }}

### Total

{{ schema_fields('types/total_resp', 'checkout') }}
Expand Down
9 changes: 6 additions & 3 deletions docs/specification/checkout-a2a.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ structured data type specified as part of UCP. When processing a payment to
complete the checkout, `payment` must be submitted to the business
agent
as a `DataPart` with attribute name `a2a.ucp.checkout.payment`. Any
associated risk signals should be sent with attribute
name `a2a.ucp.checkout.risk_signals`.
associated signals should be sent with attribute
name `a2a.ucp.checkout.signals`.

Upon completion of the checkout process, the business agent must return the
checkout object containing an `order` attribute with `id` and `permalink_url`.
Expand All @@ -251,7 +251,10 @@ checkout object containing an `order` attribute with `id` and `permalink_url`.
"a2a.ucp.checkout.payment": {
...paymentObject
},
"a2a.ucp.checkout.risk_signals":{...content}
"a2a.ucp.checkout.signals": {
"dev.ucp.buyer_ip": "203.0.113.42",
"dev.ucp.user_agent": "Mozilla/5.0 ..."
}
}
}
],
Expand Down
5 changes: 3 additions & 2 deletions docs/specification/checkout-rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,9 @@ place to set these expectations via `messages`.
}
]
},
"risk_signals": {
//... risk signal related data (device fingerprint / risk token)
"signals": {
"dev.ucp.buyer_ip": "203.0.113.42",
"dev.ucp.user_agent": "Mozilla/5.0 ..."
}
}
```
Expand Down
12 changes: 12 additions & 0 deletions docs/specification/checkout.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,18 @@ require explicit validation and error feedback.

{{ schema_fields('context', 'checkout') }}

### Signals

Transaction environment data provided by the platform to support authorization
and abuse prevention. Unlike `context` (buyer-asserted preferences) and `buyer`
(self-reported identity), signal values MUST NOT be buyer-asserted claims —
platforms provide signals based on direct observation or by relaying
independently verifiable third-party attestations. See
[Signals](overview.md#signals) for details and privacy
requirements.

{{ schema_fields('types/signals', 'checkout') }}

### Fulfillment Option

{{ extension_schema_fields('fulfillment.json#/$defs/fulfillment_option', 'checkout') }}
Expand Down
5 changes: 3 additions & 2 deletions docs/specification/examples/encrypted-credential-handler.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,9 @@ Content-Type: application/json
}
]
},
"risk_signals": {
// ... the key value pair for potential risk signal data
"signals": {
"dev.ucp.buyer_ip": "203.0.113.42",
"dev.ucp.user_agent": "Mozilla/5.0 ..."
}
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,9 @@ Content-Type: application/json
}
]
},
"risk_signals": {
// ... the key value pair for potential risk signal data
"signals": {
"dev.ucp.buyer_ip": "203.0.113.42",
"dev.ucp.user_agent": "Mozilla/5.0 ..."
}
}
```
Expand Down
107 changes: 73 additions & 34 deletions docs/specification/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -1081,26 +1081,6 @@ and cart context, then returns the resolved result. Platforms **MUST** treat the
the [Payment Handler Guide](payment-handler-guide.md#resolving-available_instruments)
for the full resolution semantics.

### Risk Signals

To aid in fraud assessment, the Platform **MAY** include additional risk signals
in the `complete` call, providing the Business with more context about the
transaction's legitimacy. The structure and content of these risk signals are
not strictly defined by this specification, allowing flexibility based on the
agreement between the Platform and Business or specific payment handler
requirements.

**Example (Flexible Structure):**

```json
{
"risk_signals": {
"session_id": "abc_123_xyz",
"score": 0.95,
}
}
```

### Implementation Scenarios

The following scenarios illustrate how different payment handlers and
Expand Down Expand Up @@ -1210,8 +1190,9 @@ POST /checkout-sessions/{id}/complete
}
]
},
"risk_signals": {
// ...
"signals": {
"dev.ucp.buyer_ip": "203.0.113.42",
"dev.ucp.user_agent": "Mozilla/5.0 ..."
}
}
```
Expand Down Expand Up @@ -1275,8 +1256,9 @@ POST /checkout-sessions/{id}/complete
}
]
},
"risk_signals": {
// ... host could send risk_signals here
"signals": {
"dev.ucp.buyer_ip": "203.0.113.42",
"dev.ucp.user_agent": "Mozilla/5.0 ..."
}
}
```
Expand Down Expand Up @@ -1353,9 +1335,9 @@ POST /checkout-sessions/{id}/complete
}
]
},
"risk_signals": {
"session_id": "abc_123_xyz",
"score": 0.95
"signals": {
"dev.ucp.buyer_ip": "203.0.113.42",
"com.example.risk_score": 0.95
},
"ap2": {
"checkout_mandate": "eyJhbGciOiJ...", // Signed proof of checkout terms
Expand Down Expand Up @@ -1437,19 +1419,17 @@ certified and handle:

### Fraud Prevention Integration

While UCP does not define fraud prevention APIs, the payment architecture
supports fraud signal integration:
UCP supports fraud prevention through [Signals](#signals) and the
payment architecture:

- Platforms provide transaction environment [signals](#signals) (IP, user
agent) on cart and checkout requests
- Businesses can require additional fields in handler configurations (e.g.,
3DS requirements)
- Platforms can submit device fingerprints and session data alongside credentials
- Payment credential providers can perform risk assessment during credential
acquisition
- Businesses can reject high-risk transactions and request additional
verification

Future extensions **MAY** standardize fraud signal schemas, but the current
architecture allows flexible integration with existing fraud prevention systems.
verification via signal feedback

### Payment Architecture Extensions

Expand Down Expand Up @@ -1580,6 +1560,65 @@ Sensitive data (such as Payment Credentials or PII) **MUST** be handled
according to PCI-DSS and GDPR guidelines. UCP encourages the use of tokenized
payment data to minimize business and platform liability.

### Signals

Businesses require transaction environment data for authorization, rate
limiting, and abuse prevention. Signal values **MUST NOT** be buyer-asserted
claims — platforms provide signals based on direct observation (e.g.,
connection IP, user agent) or by relaying independently verifiable
third-party attestations, such as cryptographically signed results from an
external verifier that the business can validate against the provider's
published key set.

All signal keys **MUST** use reverse-domain naming to ensure provenance and
prevent collisions when multiple extensions contribute to the shared namespace.
Well-known signals use the `dev.ucp` namespace (e.g., `dev.ucp.buyer_ip`);
extension signals use their own namespace (e.g., `com.example.device_id`).

```json
{
"signals": {
"dev.ucp.buyer_ip": "203.0.113.42",
"dev.ucp.user_agent": "Mozilla/5.0 ...",
"com.example.attestation": {
"provider_jwks": "https://example.com/.well-known/jwks.json",
"kid": "example-key-2026-01",
"payload": { "id": "att-7c3e9f", "pass": true, "...": "..." },
"sig": "base64url..."
}
}
}
```

Signal fields may contain personally identifiable information
(PII). Platforms **SHOULD** include only signals relevant to the current
transaction. Businesses **SHOULD NOT** persist signal data beyond the
operational needs of the transaction (e.g., order finalization, fraud review).

Businesses **MAY** use messages with code `signal` to request additional
data. The `path` field identifies the requested signal; the message `type`
determines enforcement. An `error` blocks status progression until the
signal is provided; an `info` is advisory and non-blocking.

```json
{
"messages": [
{
"type": "error",
"code": "signal",
"path": "$.signals['dev.ucp.buyer_ip']",
"content": "Buyer IP is required to proceed."
},
{
"type": "info",
"code": "signal",
"path": "$.signals['dev.ucp.user_agent']",
"content": "Providing user agent may improve checkout outcomes."
}
]
}
```

### Transaction Integrity and Non-Repudiation

For scenarios requiring cryptographic proof of authorization (e.g., autonomous
Expand Down
4 changes: 2 additions & 2 deletions docs/specification/payment-handler-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ Content-Type: application/json
}
]
},
"risk_signals": {
// risk signal objects here
"signals": {
// Platform-observed signals (buyer connection and device)
}
}
```
Expand Down
9 changes: 9 additions & 0 deletions source/schemas/shopping/cart.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@
"update": "optional"
}
},
"signals": {
"$ref": "types/signals.json",
"description": "Transaction environment data provided by the platform to support authorization decisions and abuse prevention.",
"ucp_request": {
"create": "optional",
"update": "optional"
},
"ucp_response": "omit"
},
"buyer": {
"$ref": "types/buyer.json",
"description": "Optional buyer information for personalized estimates.",
Expand Down
10 changes: 10 additions & 0 deletions source/schemas/shopping/checkout.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@
},
"ucp_response": "omit"
},
"signals": {
"$ref": "types/signals.json",
"description": "Transaction environment data provided by the platform to support authorization decisions and abuse prevention.",
"ucp_request": {
"create": "optional",
"update": "optional",
"complete": "optional"
},
"ucp_response": "omit"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you comment on why we don’t reflect these back in the response? I am not going to die on this hill, but as I mentioned in our initial discussion, I find it odd that this is one of the few platform-provided fields they would need to keep the state of themselves, since they can’t read the current values from a get_checkout call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You found the honeypot.

I don't have a strong philosophical objection to removing omit annotation on response, exactly for the reasons you mentioned. However, if you peek two lines above, you'll see that we have same pattern on context object and hence why I replicated the pattern here. I think we can make the exact same argument for context, and perhaps we should. Let's discuss on the TC call tomorrow.

},
"status": {
"type": "string",
"enum": [
Expand Down
22 changes: 22 additions & 0 deletions source/schemas/shopping/types/signals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ucp.dev/schemas/shopping/types/signals.json",
"title": "Signals",
"description": "Transaction environment data provided by the platform to support authorization decisions and abuse prevention. Values MUST NOT be buyer-asserted claims — platforms provide signals based on direct observation or independently verifiable third-party attestations. All signal keys MUST use reverse-domain naming to ensure provenance and prevent collisions when multiple extensions contribute to the shared namespace.",
"type": "object",
"propertyNames": {
"pattern": "^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$",
"description": "Reverse-domain identifier (e.g., dev.ucp.buyer_ip, com.example.device_id)."
},
"properties": {
"dev.ucp.buyer_ip": {
"type": "string",
"description": "Client's IP address (IPv4 or IPv6)."
},
"dev.ucp.user_agent": {
"type": "string",
"description": "Client's HTTP User-Agent header or equivalent."
}
},
"additionalProperties": true
}
17 changes: 1 addition & 16 deletions source/services/shopping/rest.openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -295,22 +295,7 @@
"required": true,
"content": {
"application/json": {
"schema": {
"allOf": [
{ "$ref": "#/components/schemas/checkout" },
{
"title": "Risk Signals",
"description": "Optional risk signals for fraud detection.",
"type": "object",
"properties": {
"risk_signals": {
"type": "object",
"description": "Key-value pairs of risk signals."
}
}
}
]
}
"schema": { "$ref": "#/components/schemas/checkout" }
}
}
},
Expand Down
Loading