πŸͺWebhooks

Setup powerful event-driven payment workflows in minutes.

ℹ️ To see Webhooks in action, with code examples, check out our dev examples repository.

What are Webhooks?

Helio Webhooks allow developers to listen to Helio payment events and verify transactions on your application's backend.

Security: Webhooks are linked to your Helio account. Log in to the Helio dashboard to generate your API key. Each Webhook generates a `sharedSecret` used with the target API to authenticate the endpoints with a Bearer auth header. Support: Webhooks are available for Helio Pay Links on Solana, Ethereum, Base, Polygon and BTC; as well as Pay Streams (recurring payments) on Solana.

Webhook payload example

Our webhooks include detailed information about the transaction and Pay Link.


{  // the PaylinkEventPayload object
  "transaction": "...",  // JSON string of the transactionObject below
  "event": "CREATED", // PaylinkEvents enum - matches the webhook event type when creating the webhook
  "transactionObject": {
    "id": "65e1df4d0ce08148bc333b62",  // Helio transaction ID
    "paylinkId": "65dc9f9f1154beaac39976c8", // Paylink ID
    "quantity": 1,
    "createdAt": "2024-03-01T13:59:41.303Z",
    "paymentType": "PAYLINK", // or 'PAYSTREAM'
    "meta": { // the TransactionMeta object
      "id": "65e1df4d0ce08148bc333b60",
      "amount": "9900000", // amount in minimal units (lamport/Sats/Wei)
      "senderPK": "Er3RwfYqCETBTf5RktezXaNDT3zYgwBMftxBYbX8Zk1G", // customer's wallet
      "recipientPK": "Er3RwfYqCETBTf5RktezXaNDT3zYgwBMftxBYbX8Zk1G", // merchant's recipient wallet
      "customerDetails": { 
        "country": "United States",
        "deliveryAddress": "test-address",
        "email": "test@example.com",
        "fullName": "test-full-name",
        "phoneNumber": "+0123123123123",
        "discordUsername": "a11111118100",
        "discordUser": {
          "id": "1234567890", 
          "username": "test-discord-username"
        },
        "twitterUsername": "test-x-username",
        "state": "test-state",
        "street": "test-street",
        "streetNumber": "123",
        "areaCode": "90210",
        "city": "test-city"
        "additionalJSON": "\"{\\\"test\\\": 123}\"" // any additionalJSON will be JSON stringified
      },
      "productDetails": null,
      "transactionSignature": "5AYzruixQiGX8rm279cPLo7bdqaUPYMD8Z3QnBNVz2omZHaUsUKFZRmaV8W7sAHPEyExeHkjquy8mg6LHcNktg5c",
      "transactionStatus": "SUCCESS",
      "splitRevenue": false,
      "remainingAccounts": [],
      "totalAmount": "9900000",
      "affiliateAmount": "0", // Amount sent to affiliate who referred the transaction. This will normally be "0" unless referred by affiliate
      "affiliateCode": "somecode", // Affiliate code used - this will normally be undefined
      "affiliatePublicKey": "Er3RwfYqCETBTf5RktezXaNDT3zYgwBMftxBYbX8Zk1G"; // Public key (wallet address) for affiliate who referred this transaction. This will normally be undefined
      "tokenQuote": {
        "from": "SOL",
        "fromAmountDecimal": "0.01",
        "to": "SOL",
        "toAmountMinimal": "10000000"
      },
      "submitGeolocation": "FR", // ISO 3166-1 alpha-2 country code based on IP address which submitted the transaction to our server
      "currency": {
        "id": "63430c8348c610068bcdc474",
        "blockchain": null
      }
    }
  }
}

This is an example of a curl command to register a webhook for paylinkID: 636a74162e72d4ad24ac9ce9 triggered by the CREATED event upon transaction confirmation.

This sends a POST request to targetUrl: https://target-url.com/post-endpoint.

curl -X POST \
  https://api.hel.io/v1/webhook/paylink/transaction?apiKey=YOUR_API_KEY \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_SECRET' \
  -H 'cache-control: no-cache' \
  -d '
  {
     "paylinkId": "636a74162e72d4ad24ac9ce9",
     "targetUrl": "https://target-url.com/post-endpoint",
     "events": ["CREATED"]
  }
  '

Note: For Pay Link web hooks CREATED is the only event. It's fired when the transaction is confirmed on the blockchain and within Helio's system (near-instant for Solana).

The response of this request will look as follows:

{
    "id": "636cf9c095aeedb263968ee9",
    "paylink": "636a74162e72d4ad24ac9ce9",
    "company": "6343e77d91c393456aa56462",
    "targetUrl": "https://target-url.com/post-endpoint",
    "events": [
        "CREATED"
    ]
    "sharedToken": "PGpDZdZdxjdN+VgoxR/RHItMj0gq2BK5tEZtIisfyKe4M8l0B51qED/YqSSPwfAVtonvdXKhn+9j63b4krxGWNkO3hyPQiiu1SD2uttjEAdMeUO+3QoTNAwxuH1FzwyP"
}

A unique token generated at webhook creation is included with each webhook as authorization: Bearer SHARED_TOKEN, verifying the webhook source as Helio.

Note: Retain this token to authenticate and delete web hooks if needed.

Upon successful payment on Pay Link 636cf9c095aeedb263968ee9, the targetUrl will receive an HTTP [POST] request with the following payload:

class PaylinkEventPayload {
   event: PaylinkEvents; // e.g. 'CREATED'
   transaction: string; // JSON string value of the transactionObject
   transactionObject: Transaction;
}

class Transaction {
   id: string;
   paylinkId: string;
   quantity: number;
   fee?: string;
   createdAt: string;
   paymentType: PaymentRequestType;
   claimDetails: {
       tradingView?: {
           username: string;
       };
       discord?: {
           username: string;
       };
   };
   meta: TransactionMeta;
}

 enum TransactionStatus {
  INITIATED = 'INITIATED',
  PENDING = 'PENDING',
  SUCCESS = 'SUCCESS',
  FAILED = 'FAILED',
  CANCELED = 'CANCELED',
  SETTLED = 'SETTLED',
}

class TransactionMeta {
   id: string;
   transactionSignature: string; // transaction hash on the blockchain
   amount: string;
   recipientPK: string; // merchant's public key (wallet address)
   senderPK: string; // customer's public key (wallet address)
   customerDetails: {
       email?: string;
       discordUsername?: string;
       discordUser?: {
           id: string
           username: string
       }
       twitterUsername?: string;
       fullName?: string;
       country?: string;
       deliveryAddress?: string;
       phoneNumber?: string;
       street?: string;
       streetNumber?: string;
       city?: string;
       state?: string
       areaCode?: string;
       additionalJSON?: string;
   };
   productDetails: {  // From 'advanced options' (step 3 of paylink creation) -> additional information
       name?: string;
       value?: string;
   };
   splitRevenue: boolean;
   remainingAccounts: {
       amount: string;
       recipient: string;
   }[];
   totalAmount: string;
   affiliateAmount: string;
   affiliateCode?: string;
   affiliatePublicKey?: string;
   currency: {
       id: string;
       blockchain?: { engine: BlockchainEngine } | null;
   };
   transactionType?: TransactionType;
   tokenQuote?: TokenQuoteMeta;
   transactionStatus: TransactionStatus // e.g. 'SUCCESS'
}

// used for converting between pricing currency (from) and what they paid with (to)
// note: from could be a crypto currency (e.g. (0.1 ETH), or fiat currency (e.g. $5)
// to is always going to be a crypto currency.
// This is useful when using swaps, to see what they paid
class TokenQuoteMeta {
   from: string;
   fromAmountDecimal: string;
   to: string;
   toAmountMinimal: string;
}

class BlockchainEngine {
  id: string;
  type: BlockchainEngineType;
}

enum PaymentRequestType {
   PAYLINK = "PAYLINK",
   PAYSTREAM = "PAYSTREAM",
}

enum PaylinkEvents {
   CREATED = "CREATED",
}

enum TransactionType {
   REFUND = "REFUND",
   PAYLINK = "PAYLINK",
}

enum BlockchainEngineType {
   EVM = "EVM",
   SOL = "SOL",
   BTC = "BTC",
}

Note: When you create or list Pay Link webhooks, a sharedToken is generated. This token is sent to your endpoint as authorization: Bearer SHARED_TOKEN to validate that the webhook originates from Helio.

You can list all the hooks that are registered for a Pay Link with the following curl request:

curl \
https://api.hel.io/v1/webhook/paylink/transaction?apiKey=YOUR_API_KEY&paylinkId=636501dad706741f1ad63770 \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_SECRET' \
  -H 'cache-control: no-cache' 

This will return an array of all Webhooks that are registered for the Pay Link. In case the API key isn't associated with Pay Link, an unauthorised request will be thrown.

[
    {
        "id": "636a3838591b00789e778b79",
        "creator": undefined, // for legacy webhooks
        "paylink": "636501dad706741f1ad63779",
        "company": "6343e77d91c393456aa56422",
        "targetUrl": "https://targetUrl.com",
        "events": [
            "CREATED"
        ]
        "sharedToken": "1HSY3MM4I1hvIRp2xv83uTY3J55nIE66vuaBrutBoCCr3fChi8rY458"
    }
]

You can delete any Webhook ID by calling the delete endpoint:

curl -X DELETE \
https://api.hel.io/v1/webhook/paylink/transaction/636a75ee5a9078bb9803a681?apiKey=YOUR_API_KEY
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_API_SECRET' \
  -H 'cache-control: no-cache' 

To be clear, in this example, 636a75ee5a9078bb9803a681is the ID of the Webhook. You can get this when you create a webhook, or by listing all webhooks for a Pay Link

Pay Stream examples

Pay Streams are recurring payments, great for subscriptions or gated content. For streamId 636a74162e72d4ad24ac9ce9, a similar curl command can be used to register recurring payment webhooks. The POST request is sent to targetUrl: https://target-url.com/post-endpoint.

curl -X POST \
  https://api.hel.io/v1/webhook/stream/transaction?apiKey=<apiKey> \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {apiSecret}' \
  -H 'cache-control: no-cache' \
  -d '
  {
     "streamId": "636a74162e72d4ad24ac9ce9",
     "targetUrl": "https://target-url.com/post-endpoint",
     "events": ["STARTED", "ENDED"]
  }
  '

In the following example we register a Webhook to follow two events: stream STARTED, and stream ENDED

The response of this request will look as follows:

{
    "id": "636cf9c095aeedb263968ee9",
    "stream": "636a74162e72d4ad24ac9ce9",
    "company": "6343e77d91c393456aa56462",
    "targetUrl": "https://target-url.com/post-endpoint",
    "events": [
        "STARTED",
        "ENDED"
    ]
    "sharedToken": "security token"
}

When starting a stream on id 636cf9c095aeedb263968ee9 the targetUrl will receive the HTTP [POST] request with the following payload StreamEventPayload

export class StreamEventPayload {
  event: StreamTransactionEvent;
  transaction: PaystreamTransaction;
}

export class PaystreamTxWithMeta {
  meta: {
    id: string;
    transactionSignature: string;
    amount: string;
    recipientPK: string;
    senderPK: string;
    customerDetails: {
      email?: string;
      discordUsername?: string;
      twitterUsername?: string;
      fullName?: string;
      country?: string;
      deliveryAddress?: string;
      phoneNumber?: string;
      street?: string;
      streetNumber?: string;
      city?: string;
      areaCode?: string;
      additionalJSON?: string;
    };
    productDetails: {
      name?: string;
      value?: string;
    };
    splitRevenue: boolean;
    remainingAccounts: {
      amount: bigint;
      recipient: string;
    }[];
    totalAmount: number;
  };

  withdrawals: {
    paystreamTx: string;
    amount: string;
    fee?: string;
  }[];

  fee: string;

  paystream: string;

  id: string;

  paymentAccount: string;

  startedAt: {
    timestamp: string;
    transactionSignature: string;
  };

  createdAt: string;

  canceledAt: {
    timestamp: number;
    transactionSignature: string;
  } | null;

  endedAt: string;

  interval: number;

  paymentType: PaymentRequestType;

  active: boolean;

  isHelioX?: boolean;
}

export enum PaymentRequestType {
  PAYLINK = 'PAYLINK',
  PAYSTREAM = 'PAYSTREAM',
}

export enum StreamTransactionEvent {
  STARTED = 'STARTED',
  ENDED = 'ENDED',
}

Listing Pay Stream webhooks

You can list all the hooks that are registered for a stream with the following curl reuqest:

curl \
https://api.hel.io/v1/webhook/stream/transaction?apiKey=Uus~N8ePqcG8jaYxFfGajGu3US.Y8XlicIZwEoUqT~YGy7A7qjE-o-QXDN1N7MwF&streamId=636501dad706741f1ad63770
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {apiSecret}' \
  -H 'cache-control: no-cache' 

This will return an array of all Webhooks that are registered for the Pay Stream. In case the API key isn't associated with stream, an unauthorised request error will be thrown.

[
    {
        "id": "636a3838591b00789e778b79",
        "stream": "636501dad706741f1ad63779",
        "company": "6343e77d91c393456aa56422",
        "targetUrl": "https://targetUrl.com",
        "events": [
            "STARTED",
            "ENDED"
        ]
    }
]

Last updated