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
}