Webhook Guide

Webhooks let you receive real-time notifications when events occur in your Neutron account — primarily transaction status changes. Instead of polling the API, Neutron pushes updates directly to your server.

How It Works

  1. Register a webhook — POST /api/v2/webhook with your callback URL and secret
  2. Transaction changes — Neutron sends a POST request to your callback URL
  3. Verify the signature — validate using your webhook secret
  4. Respond with 200 — acknowledge receipt

Setting Up a Webhook

1. Create a Webhook

Register your callback URL and a secret used for signature verification:

{
  "callback": "https://yourapp.com/webhooks/neutron",
  "secret": "your-webhook-secret-key"
}

Response:

{
  "id": "01e2a3dc-0c34-4e14-92e4-b270c4778d95",
  "callback": "https://yourapp.com/webhooks/neutron",
  "createdAt": 1747889654376
}

Save the id — you need it to update or remove the webhook later.

2. Receive Events

When a transaction changes state, Neutron sends a POST request to your callback URL with the transaction details in the body. The request includes a signature header for verification.

3. Verify the Signature

Every webhook request includes a signature generated using the secret you provided when creating the webhook. Always verify this signature before processing the event.

To verify:

  1. Read the raw request body
  2. Compute HMAC-SHA256 of the body using your webhook secret
  3. Compare with the signature in the request header

Node.js:

const crypto = require('crypto');

function verifyWebhookSignature(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Python:

import hmac
import hashlib

def verify_webhook(body, signature, secret):
    expected = hmac.new(
        secret.encode(),
        body if isinstance(body, bytes) else body.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Security: Always use constant-time comparison (timingSafeEqual or compare_digest) to prevent timing attacks.

4. Respond

Return a 200 OK response promptly. If your server doesn't respond with a 2xx status code, Neutron will retry the delivery.

Webhook Events

Webhooks fire on transaction state changes. The payload includes the full transaction object with its current state. Key states to handle:

StateMeaningAction
quotedTransaction created, awaiting confirmationInformational
srccreatedSource side initiatedPayment is being processed
destsentDestination payout sentFunds are on the way
completedTransaction fully completedMark as successful
failedTransaction failedHandle the failure
cancelledTransaction was cancelledClean up

See Transaction Status Types for the complete list.

Best Practices

Idempotency

You may receive the same event more than once (e.g., due to retries). Design your webhook handler to be idempotent — processing the same event twice should have the same result as processing it once.

Tip: Track processed transaction IDs and skip duplicates.

Quick Response

Process webhooks asynchronously. Accept the webhook immediately (return 200), then process the event in a background job. Don't do heavy work in the webhook handler — Neutron may time out waiting for your response.

Do this: Receive, queue, return 200, then process in background.

Not this: Receive, do heavy processing, then return 200 (slow, may timeout).

Error Handling

If your endpoint is temporarily unavailable, Neutron will retry delivery with exponential backoff. Make sure your endpoint can handle occasional bursts of events if it comes back online after downtime.

HTTPS Only

Always use HTTPS for your callback URL. Webhook payloads contain sensitive transaction data.

Managing Webhooks

You can only have one active webhook per account. To change your callback URL or secret, use the update endpoint.