> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroclick.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# End-to-end body encryption

> How buyers and sellers encrypt request and response bodies so ZeroClick relays ciphertext without access to either private key.

<Check>
  End-to-end body encryption is available in production now. ZeroClick relays Compact JWE ciphertext and never receives the private keys that decrypt either body.
</Check>

ZeroClick terminates TLS at the pay URL to handle routing, identity, and payment. Without body encryption, the proxy receives the application body plaintext.

Encrypted calls add a Compact JWE envelope inside TLS. The buyer creates this envelope before the request reaches ZeroClick. The seller opens it after the proxy forwards it.

The response follows the reverse path. The seller encrypts the response body for a one-time buyer key. ZeroClick returns the ciphertext, and the buyer decrypts it.

## The data path

```mermaid theme={null}
sequenceDiagram
    participant B as Buyer
    participant Z as ZeroClick proxy
    participant S as Seller API

    S->>Z: Publish public P-256 JWK
    Note right of S: Seller private key stays here
    B->>Z: Fetch encryption guide and public JWKS
    Z-->>B: Active kid and public JWK
    Note left of B: Create one-time reply key pair<br/>Encrypt request body for seller
    B->>Z: Compact JWE request body
    Note over Z: Validate protected header<br/>Hash and sign ciphertext
    Z->>S: Same ciphertext bytes + zc-signature
    Note right of S: Verify and check allowance<br/>Then decrypt and serve
    S-->>Z: Response body encrypted for buyer
    Z-->>B: Same response ciphertext bytes
    Note left of B: Decrypt with reply private key
```

<Steps>
  <Step title="The seller publishes a public key">
    The seller generates a P-256 key pair in its own infrastructure. It publishes only the public JWK through ZeroClick.

    ZeroClick rejects JWKs that contain private parameters. The seller private key stays in the seller's key manager or application environment.
  </Step>

  <Step title="The buyer encrypts the request body">
    The buyer reads the active `kid` and public JWKS from the storefront. It also creates a fresh P-256 reply key pair.

    The buyer encrypts the body as Compact JWE. The protected header carries the seller `kid`, original content type, and public reply JWK.
  </Step>

  <Step title="ZeroClick processes ciphertext">
    The proxy can read the protected header without a private key. It validates the fixed cipher suite, seller `kid`, and public reply key.

    ZeroClick hashes the original ciphertext for payment binding and `zc-signature`. It forwards those ciphertext bytes without modification.
  </Step>

  <Step title="The seller verifies and decrypts">
    The seller verifies `zc-signature` against the ciphertext before decryption. It also checks the buyer's allowance before it does application work.

    After an allow decision, the seller selects its private key by `kid` and decrypts the body inside its own infrastructure.
  </Step>

  <Step title="The seller encrypts the response body">
    The seller encrypts the application response for the public reply key. Only the buyer has the matching private key.

    ZeroClick relays the response ciphertext. The buyer uses its one-time private key to decrypt the body.
  </Step>
</Steps>

## What ZeroClick can and cannot see

The proxy still needs transaction metadata to route the call and settle payment. Body encryption changes the body trust boundary, not the entire HTTP exchange.

| Data                                                | Available to ZeroClick?                        | Why                                                                                                          |
| --------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Request body plaintext                              | No                                             | The seller holds the only private key that opens the request JWE.                                            |
| Response body plaintext                             | No, when the buyer requests an encrypted reply | The buyer keeps the private reply key.                                                                       |
| Seller private key                                  | No                                             | The seller publishes a strict public-only JWKS.                                                              |
| Buyer reply private key                             | No                                             | The buyer sends only the public half in the JWE protected header.                                            |
| Request and response ciphertext                     | Yes                                            | The proxy must relay these bytes. Ciphertext length is also visible.                                         |
| Method, host, path, query, HTTP headers, and status | Yes                                            | These values remain outside the JWE body envelope.                                                           |
| JWE protected header                                | Yes                                            | It exposes the suite, `kid`, original content type, and public reply JWK. It does not expose body plaintext. |
| Identity, payment, allowance, and usage data        | Yes                                            | ZeroClick needs this control data to authenticate and settle the transaction.                                |

The proxy request record stores the method, path and query, status, identifiers, and a SHA-256 body digest. It has no request or response body field.

The proxy cannot log or persist body plaintext that it never receives. It can log or persist visible metadata and ciphertext.

Without an endpoint's private key, retained ciphertext remains unreadable. This is a cryptographic limit, not a logging policy.

## Wire contract

ZeroClick accepts one body-encryption profile:

| Field                                     | Required value                                               |
| ----------------------------------------- | ------------------------------------------------------------ |
| Request and encrypted-response media type | `application/jose`                                           |
| JWE serialization                         | Compact JWE with five base64url segments                     |
| Seller and reply keys                     | EC P-256 public JWKs                                         |
| Key management                            | `ECDH-ES+A256KW`                                             |
| Content encryption                        | `A256GCM`                                                    |
| Seller key selection                      | `kid` in the protected header                                |
| Original body media type                  | `cty` in the protected header                                |
| Buyer reply key                           | `https://zeroclick.io/jwe/reply-jwk` in the protected header |

The fixed suite prevents algorithm negotiation. The protected header is authenticated, so a relay cannot replace the reply key without invalidating the request JWE.

The payment challenge binds to a SHA-256 digest of the ciphertext. After a `402`, the buyer must retry with the identical JWE bytes.

<Warning>
  Do not encrypt the same plaintext again for the paid retry. JWE uses random values, so a new envelope has a different digest.
</Warning>

ZeroClick protocol responses remain plaintext. These include payment challenges and errors produced before the seller decrypts the request.

## Scope and trust boundaries

* Body encryption is opt-in for each request. A plaintext request remains plaintext at the proxy.
* Response encryption is a separate opt-in. The buyer must include a public reply JWK, and the seller must use it.
* The JWE protects bodies only. It does not hide routes, headers, status codes, timing, length, billing data, or usage quantities.
* This design protects body confidentiality from the ZeroClick data plane. It does not protect a compromised buyer or seller endpoint.
* Storefront key discovery trusts the public key that ZeroClick serves for the seller. Buyers can pin a seller key through a seller-controlled channel.
* The response JWE provides confidentiality, but it is not a seller signature. If the buyer must prove response origin, add an application-level signature.

## Configure a seller

<Steps>
  <Step title="Generate a P-256 encryption key pair">
    Generate the key pair in the seller's infrastructure. Give the public JWK a unique `kid`. Keep the private JWK secret.
  </Step>

  <Step title="Publish the public JWKS">
    In the dashboard, open **Stores → your seller → Store settings → Upstream security → Body encryption**. Paste the public JWKS.

    You can also call `PUT /v1/sellers/{sellerId}/body-encryption` with an `admin:write` API key.
  </Step>

  <Step title="Add the seller-side helpers">
    Verify the encrypted request before decryption. Check the allowance before decryption. Attach `zc-usage` before you encrypt the response.

    <Columns cols={2}>
      <Card title="TypeScript" icon="hexagon" href="/sdks/typescript/api#encryption">
        Use `decryptRequest` and `encryptResponse`.
      </Card>

      <Card title="Python" icon="hexagon" href="/sdks/python/api#encryption">
        Use `decrypt_request` and `encrypt_response`.
      </Card>

      <Card title="Go" icon="hexagon" href="/sdks/go/api#encrypted-request-bodies">
        Use the separate `jwe` package.
      </Card>

      <Card title="Ruby" icon="hexagon" href="/sdks/ruby/api#encrypted-request-bodies">
        Require `zeroclick/sellers/encryption`.
      </Card>
    </Columns>
  </Step>

  <Step title="Give buyers the generated guide">
    Each configured pay URL publishes `/encryption.md` and `/.well-known/jwks.json`. The guide contains a working buyer example for that seller.
  </Step>
</Steps>

During key rotation, publish the old and new public keys together. Keep both private keys available until all requests and payment retries drain.
