Skip to main content
SDKs & Libraries

Go SDK

The official XRNotify Go SDK provides an idiomatic, context-aware client for managing webhooks, querying deliveries, and verifying signatures. Requires Go 1.21 or later. All methods accept a context.Context for cancellation and deadline propagation.

Installation

go get github.com/xrnotify/xrnotify-go

Client setup

Create a client with your API key. The client is safe for concurrent use across goroutines.

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/xrnotify/xrnotify-go"
)

func main() {
    client := xrnotify.NewClient(os.Getenv("XRNOTIFY_API_KEY"))

    // Optional: use test environment
    // client := xrnotify.NewClient(
    //     os.Getenv("XRNOTIFY_TEST_KEY"),
    //     xrnotify.WithEnvironment(xrnotify.EnvironmentTest),
    // )

    ctx := context.Background()
    _ = client
    _ = ctx
}

Thread safety: A single *xrnotify.Client can be shared across goroutines. Create one at startup and reuse it throughout the lifetime of your application.

Creating a webhook

Register a webhook endpoint. The returned struct includes Secret — this field is only populated on the creation response.

webhook, err := client.Webhooks.Create(ctx, &xrnotify.CreateWebhookParams{
    URL:            "https://yourapp.com/webhooks/xrpl",
    EventTypes:     []string{"payment.xrp", "nft.minted"},
    AccountFilters: []string{"rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"},
    Description:    "Payment processor",
})
if err != nil {
    log.Fatalf("create webhook: %v", err)
}

fmt.Println("ID:", webhook.ID)
fmt.Println("Secret:", webhook.Secret) // Store immediately — only available once

Important: webhook.Secret is only set in the creation response. Subsequent fetches of the same webhook will return an empty string for this field.

Listing and managing webhooks

List, update, and delete webhooks. The list method returns a paginated result; use result.HasMore and StartingAfter to page through results.

// List webhooks
result, err := client.Webhooks.List(ctx, &xrnotify.ListWebhooksParams{
    Limit: 10,
})
if err != nil {
    log.Fatal(err)
}
for _, wh := range result.Data {
    fmt.Println(wh.ID, wh.URL, wh.IsActive)
}

// Paginate
if result.HasMore {
    lastID := result.Data[len(result.Data)-1].ID
    nextPage, _ := client.Webhooks.List(ctx, &xrnotify.ListWebhooksParams{
        Limit:         10,
        StartingAfter: lastID,
    })
    _ = nextPage
}

// Update a webhook
active := false
_, err = client.Webhooks.Update(ctx, "wh_abc123", &xrnotify.UpdateWebhookParams{
    IsActive: &active,
})

// Delete a webhook
err = client.Webhooks.Delete(ctx, "wh_abc123")

Listing deliveries

import "time"

since := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
deliveries, err := client.Deliveries.List(ctx, &xrnotify.ListDeliveriesParams{
    WebhookID: "wh_abc123",
    Status:    "failed",
    Since:     &since,
})
if err != nil {
    log.Fatal(err)
}
for _, d := range deliveries.Data {
    fmt.Println(d.ID, d.EventType, d.StatusCode, d.LastError)
}

Verifying signatures

Use the verify sub-package to validate incoming webhook requests. The function uses hmac.Equal for constant-time comparison.

package main

import (
    "encoding/json"
    "io"
    "log"
    "net/http"
    "os"

    "github.com/xrnotify/xrnotify-go"
    "github.com/xrnotify/xrnotify-go/verify"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Bad request", http.StatusBadRequest)
        return
    }

    ok := verify.Signature(
        body,
        r.Header.Get("X-XRNotify-Signature"),
        os.Getenv("WEBHOOK_SECRET"),
    )
    if !ok {
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    var event xrnotify.Event
    if err := json.Unmarshal(body, &event); err != nil {
        http.Error(w, "Bad request", http.StatusBadRequest)
        return
    }

    log.Printf("Received event: %s in ledger %d", event.EventType, event.LedgerIndex)

    switch event.EventType {
    case "payment.xrp":
        // unmarshal event.Payload into xrnotify.PaymentXrpPayload
    case "nft.minted":
        // unmarshal event.Payload into xrnotify.NftMintedPayload
    }

    w.WriteHeader(http.StatusOK)
}

func main() {
    http.HandleFunc("/webhooks/xrpl", webhookHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Raw body required: Read the body with io.ReadAll before calling verify.Signature. Do not use a JSON decoder directly on r.Body before verification.

Struct types

The SDK provides typed structs for all event envelopes and payloads. The Payload field is a json.RawMessage so you can unmarshal it into the appropriate typed struct based on EventType.

// Event envelope — same structure for all event types
type Event struct {
    EventID     string          `json:"event_id"`
    EventType   string          `json:"event_type"`
    LedgerIndex int64           `json:"ledger_index"`
    Timestamp   time.Time       `json:"timestamp"`
    Network     string          `json:"network"`
    WebhookID   string          `json:"webhook_id"`
    Payload     json.RawMessage `json:"payload"`
}

// Typed payload structs
type PaymentXrpPayload struct {
    Sender         string  `json:"sender"`
    Receiver       string  `json:"receiver"`
    Amount         string  `json:"amount"`
    AmountXRP      string  `json:"amount_xrp"`
    Fee            string  `json:"fee"`
    DeliveredAmount string `json:"delivered_amount"`
    DestinationTag *int64  `json:"destination_tag"`
    SourceTag      *int64  `json:"source_tag"`
    TxHash         string  `json:"tx_hash"`
    LedgerIndex    int64   `json:"ledger_index"`
    Sequence       int64   `json:"sequence"`
}

Related