Skip to main content

Payment Rules

A payment rule lets you require senders to pay a token amount when they open a thread to your inbox. This is the primary spam-prevention and monetisation mechanism in Packet.

Why payment rules exist

Without a payment rule, anyone can open a thread to you for free. With one, every new thread costs the sender a configurable amount of tokens — paid either directly to you or held in escrow until you approve the conversation.

The rule is enforced on-chain: a thread creation that doesn't include the correct payment simply fails.

Direct vs escrow

Direct payment — funds go straight to the recipient's token account when the thread is created. Simple, immediate, non-reversible.

Escrow payment — funds are held in the inbox's associated token account. Released when both parties approve, or when a time lock expires. If you reject the conversation, the sender can reclaim their tokens. This is better for high-value inboxes where you want to review threads before committing.

Setting up a payment rule

Pass payment to createInbox:

await client.createInbox({
inboxId: 1,
payment: {
amount: new BN(5_000_000), // 5 USDC (6 decimals)
mint: usdcMint,
escrowEnabled: false, // direct payment
},
});

With escrow:

await client.createInbox({
inboxId: 1,
payment: {
amount: new BN(5_000_000),
mint: usdcMint,
escrowEnabled: true, // funds held until released
// `to` must not be set when escrow is enabled
},
});

CreateInboxPaymentParams

FieldTypeRequiredDescription
amountBNYesRequired payment per new thread
mintPublicKeyYesToken mint
escrowEnabledbooleanYesHold in escrow or pay directly
tosee belowNoRecipient token account (not valid with escrow)
tokenProgramPublicKeyNoAuto-detected from the mint owner if omitted

to — recipient token account

When escrowEnabled is false, funds go to a token account you specify:

TypeDescription
{ type: "ata"; owner?: PublicKey }Associated token account of owner (defaults to inbox owner)
{ type: "raw"; address: PublicKey }An explicit token account address used as-is
{ type: "custom"; keypair: Keypair; owner?: PublicKey }A new token account is created with this keypair

If to is omitted entirely, it defaults to the inbox owner's ATA for the given mint.

How senders interact with payment rules

When a sender calls createThread with a targetInbox, the SDK auto-reads the inbox's payment rule and fills in the payment accounts automatically. Senders don't need to manually specify payment in the common case:

const recipientInbox = await client.inbox(recipientInboxAddress);

const { client: thread } = await client.createThread({
to: recipientPublicKey,
targetInbox: recipientInbox.Inbox, // payment auto-filled
messageType: MessageType.Text,
content: "Hello!",
});

If the sender wants to override or disable the payment explicitly, they can pass payment directly to createThread. Passing { disable: true } skips the payment — which will cause the transaction to fail if the rule is enforced on-chain.

Editing or removing a payment rule

Use inbox.editPayment() to update the rule after the inbox has been created, or pass null to remove it entirely:

// Switch from direct to escrow
await inbox.editPayment({
payment: {
amount: new BN(5_000_000),
mint: usdcMint,
escrowEnabled: true,
},
});

// Remove payment requirement
await inbox.editPayment({ payment: null });

editPayment accepts the same payment shape as createInbox, plus null to clear.

Returns Promise<TxReceiptWithClient<InboxClient>>


The PaymentRule type

interface PaymentRule {
inner: Payment; // amount + mint + to
escrow: Escrow | null; // null for direct payments
tokenProgram: TokenProgramType;
}

interface Payment {
amount: BN;
mint: PublicKey;
to: PublicKey; // the token account address, not the owner
}

interface Escrow {
releaseSeconds: BN; // lock duration
}