Paper Checkout
Search…
Webhooks
You can configure your checkout with up to three webhook URLs. When a user successfully purchases and receives an NFT through your checkout, each webhook URL will be called.
Only HTTPS webhook URLs are supported.
A webhook is typically called immediately after a successful purchase but may be delayed by up to a few minutes depending on network traffic.
​

HTTPS Request

Paper's server will make the following HTTPS request to each webhook URL you define. Please ensure your backend handles this route, asserts the correct HTTP method, and decodes the signature.
  • Method: POST
  • Header:
    • Content-Type: application/json
    • X-Paper-Signature: <signature>
      • Your backend should decode this signature to ensure it is trusted. See Verify the Signature Header.
      • HTTP header names should be treated as case-insensitive. Some popular web frameworks may convert the name of this header to lowercase.
  • Body payload:
1
{
2
"event": "transfer:succeeded",
3
"result": {
4
"id": "8e2b245b-7be7-4f25-a406-cc9a3c905f9f",
5
"checkoutId": "d2ccf22b-a3d5-46b8-9da5-55a28016ab52",
6
"walletAddress": "0x86Bd910E7d7D84C0F8C8DE418B877609196Fb719",
7
"walletType": "MetaMask",
8
"email": "[email protected]",
9
"quantity": 1,
10
"paymentMethod": "BUY_WITH_CARD",
11
"networkFeeUsd": 0.01,
12
"totalPriceUsd": 45.50,
13
"createdAt": "2022-03-08T12:28:07.958+00:00",
14
"paymentCompletedAt": "2022-03-08T12:28:32.528+00:00",
15
"transferCompletedAt": "2022-03-08T12:28:52.265+00:00",
16
"claimedTokens": {
17
"tokenIds": ["0"],
18
"transactionHashes": {
19
"0x4c76e13a67e4ce1a03ae111f862f82d69383f6ac7aaf7f248644d9818d33cf19": [
20
"0"
21
]
22
}
23
}
24
}
25
}
Copied!

Transaction Data Model

The "results" field contain the following fields (i.e. the transaction id is requestBody.result.id).
Field
Type & Example
Notes & Example
id
string 773c9754-6ec1-4241-ba52-309f80b901d7
The unique ID of the transaction.
checkoutId
string 70e08b7f-c528-46af-8b17-76b0e0ade641
The ID of the checkout.
walletAddress
string 0xdf8d13972902A6FCc5Ad61b3ec86737d6F16fb29
The buyer's wallet where the NFT was minted or transferred to.
walletType
string, one of Preset Paper Wallet (for ERC-20 chains) MetaMask WalletConnect Coinbase Wallet (for Solana) Phantom
The type of the buyer's wallet.
email
string, optional [email protected]
The buyer's email, if provided. This may not be set if the user connected an external wallet and did not provide an email on the "Purchase confirmed" page.
quantity
number 1
The number of NFTs purchased.
networkFeeUsd
number, in USD 33.50
The amount paid for network fees by the user.
​
This value is not set for native mints.
totalPriceUsd
number, in USD 33.50
The total amount paid by the user.
createdAt
string, in RFC3339 2022-02-04T02:32:45.260325+00:00
The time when the user started their transaction.
paymentCompletedAt
string, in RFC3339 2022-02-04T02:32:45.260325+00:00
The time when the user completed their payment.
transferCompletedAt
string, in RFC3339 2022-02-04T02:32:45.260325+00:00
The time when the user received their purchased NFT.
paymentMethod
string, one of NATIVE_MINT BUY_WITH_CARD BUY_WITH_CRYPTO
The buyer's payment method.
claimedTokens
object in the following format
1
{
2
"tokenIds": ["0"],
3
"transactionHashes": {
4
"0x4c76e13a67e4ce1a03ae111f862f82d69383f6ac7aaf7f248644d9818d33cf19": [
5
"0"
6
]
7
}
8
}
Copied!
The blockchain transaction hashes of the tokens claimed by this purchase, and the list of all tokens claimed.
​

Event Types

Event
Description
payment:succeeded
The purchase is confirmed. Paper has received payment from the buyer. This event is only sent for BUY_WITH_CARD and BUY_WITH_CRYPTO payment methods.
transfer:succeeded
The purchase is completed and the NFT(s) has been transferred to the buyer's wallet.
transfer:failed
The purchase did not complete after multiple attempts, and the NFT(s) was not transferred to the buyer's wallet. The Paper team is alerted when this happens and will manually retry this job.
​

Verify the Signature Header

Paper signs each webhook request with a signature provided in the X-Paper-Signature header. This signature ensures you can trust that the request comes from Paper and not a malicious user.
To verify the signature, create a SHA-256 HMAC hash with the API key as the secret and the body payload (as a JSON-encoded string) as the message.
​
Here's an example in Node with Typescript (simplified for clarity):
1
import { createHmac, timingSafeEqual } from 'crypto';
2
​
3
// In your webhook HTTP handler:
4
const signature = req.headers['X-Paper-Signature'];
5
const hash = createHmac('sha256', YOUR_API_KEY)
6
.update(JSON.stringify(req.body)) // {"event":"transfer:succeeded","result":{"id":...
7
.digest('hex');
8
​
9
if (!timingSafeEqual(Buffer.from(signature), Buffer.from(hash))) {
10
// Signature mismatch: Reject this request
11
} else {
12
// Signature match: Continue processing this request
13
}
Copied!
​

Troubleshooting

Here are some common problems if your webhook call is not successful.
  • Check if your server framework is reading the header properly. Some frameworks like Next.js lowercase all header names.
  • Make sure you're passing the entire body as the message in the HMAC signature. Some frameworks require you to configure the middleware to not parse the request body.
  • Make sure the API key you're using matches the one shown in the dashboard.
​

Retries

Each webhook will be attempted until a 2xx status code is returned. If a 2xx is not returned, the webhook will retry once every 5 minutes for one hour (13 attempts in total).
​