# Encrypt a note to a published viewing key

> Skip the keystore lookup and encrypt directly against a viewing pubkey you already know.

## What this lets you do

Send a note directly to a known viewing pubkey, bypassing the on-chain [`Keystore`](/protocol/contracts/keystore) lookup. Two cases need this:

-   **[Payment requests](/operations/payment-requests).** The payer encrypts to the request's per-request public key, which the recipient derives deterministically and includes in the request artifact rather than publishing in the keystore.
-   **P2P off-chain key exchange.** Two parties exchanged viewing pubkeys directly (e.g., via a secure channel) and want to skip keystore registration.

## Constraints & limits

-   **Caller supplies the noteSecretsMap.** Unlike the evmAddress variant, where the SDK generates noteSecrets, this variant requires you to provide them. Use [`CryptoService`](/sdk/crypto-services) to generate cryptographically random 32-byte secrets.
-   **No on-chain attestation that the pubkey belongs to anyone.** The keystore registration is what proves "this address claims this viewing key." Encrypting directly bypasses that proof, so only use this path when you trust where the pubkey came from.
-   **This is the direct public-key path, not payment-request fulfillment.** The `noteSecretsMap` variant shown here is for sends outside a payment request, where you generate the secrets yourself and deliver them [out-of-band](/concepts/discoverable-vs-oob). Fulfilling a payment request works differently, because you hold only the request's pre-committed `noteAddressHashes` and never see the secrets. In that case you pass the hashes through the `{publicViewingKey, noteAddressHashes, hint}` variant with `hint` set to the request's `tag`, or you call `session.prepareFulfillPaymentRequest(...)`, which wires that up for you.

## What it unlocks next

-   [Payment requests](create-payment-request), since this is the encryption mechanism the request flow uses under the hood.
-   Trustless P2P flows where both sides exchange pubkeys out-of-band and skip keystore gas costs.
-   Privacy-preserving group payouts: one sender pays many recipients, each with their own pubkey already shared off-chain.

## How to use it

```ts

const crypto = new CryptoService();
const hashService = await PoseidonHashService.create();

const noteSecret = crypto.generateSecret();
const noteSecretsMap = new Map();
const recipientNoteAddressHash = hashService.hash([recipientAddress, noteSecret]);
noteSecretsMap.set(recipientNoteAddressHash, noteSecret);

const prepared = await session.prepareTransfer({
    inputCommitments: [noteToSpend.commitment],
    amount,
    tokenId,
    recipientDiscoveryData: {
        publicViewingKey: recipientPubkey,
        noteSecretsMap,
        hint: routingHint,
    },
});

const result = await session.relayTransfer(prepared.relayOptions[0]);
```

`noteAddressHash` binds the note to the recipient's EVM address, so compute it with the SDK's Poseidon hash service. Payment-request fulfillment uses the `noteAddressHashes` variant or `session.prepareFulfillPaymentRequest(...)` instead.

## Behind the scenes

SDK call `session.prepareTransfer({recipientDiscoveryData: {publicViewingKey, noteSecretsMap, hint?}})`, variant 2 of `RecipientDiscoveryData`
Cryptography The SDK generates an ephemeral X25519 keypair per output note, runs ECDH against the supplied publicViewingKey, derives an encryption key, and encrypts the note payload. The hint is *not* encrypted. It's emitted separately as the event's public indexed topic.
On-chain effect A `Note(hint, encryptedData)` event, where the `hint` is a public, indexed routing topic (a chosen tag for this direct send) and `encryptedData` is the sealed payload. The event has the same shape as registered-recipient transfers, just with a chosen hint instead of the recipient's auto-derived one.
