Help center

Beginner guide

How to receive a webhook on your server

No prior webhook experience needed. By the end of this page you'll have a working listener, a public URL, and a webhook registered in your Ultraner console.

Prefer to have an AI walk you through it?

What is a webhook?

A webhook is Ultraner reaching out to your server the moment something happens, a payment succeeds, a subscription renews, a transfer completes, instead of you constantly asking "is it done yet?". You give Ultraner a URL once, and from then on Ultraner sends a small HTTP request to that URL every time a matching event occurs. All you need is a URL that's always reachable and a few lines of code to read what arrives.

1. Create a listener

A listener is just a normal web server with one route that accepts POST requests. Pick your language below, this is a complete, runnable file, copy it as a starting point:

Code
const express = require("express");
const crypto = require("crypto");
const app = express();

// Ultraner needs the RAW request body to verify the signature correctly,
// don't use express.json() on this route.
app.use(express.raw({ type: "application/json" }));

app.post("/webhooks/ultraner", (req, res) => {
  const signature = req.headers["x-ultraner-signature"];
  const expected = "sha256=" +
    crypto.createHmac("sha256", process.env.ULTRANER_WEBHOOK_SECRET)
          .update(req.body)
          .digest("hex");

  if (signature !== expected) {
    return res.status(401).send("Invalid signature");
  }

  // Respond fast, then handle the event, Ultraner retries if you're slow.
  res.status(200).send("OK");

  const { event, data } = JSON.parse(req.body);
  console.log("Received event:", event, data);
  // TODO: update your database, unlock access, send a receipt, etc.
});

app.listen(3000, () => console.log("Listening on port 3000"));

What this file actually does

  • Accepts a POST request at one URL, e.g. /webhooks/ultraner.
  • Recomputes the signature from the raw request body and your secret, and compares it to the X-Ultraner-Signature header, this proves the request really came from Ultraner and wasn't tampered with.
  • Responds 200 immediately, then does the real work, slow responses cause Ultraner to retry and can create duplicate processing.
  • Reads event (e.g. payment.success) and data from the body to decide what to do.

2. Get a public URL

Ultraner needs to reach your server from the internet, localhost alone won't work.

While developing locally, use a tunnel like ngrok to get a temporary public HTTPS URL for your local server:

Code
ngrok http 3000

# ngrok prints a URL like:
# https://a1b2-c3d4.ngrok-free.app
# Use https://a1b2-c3d4.ngrok-free.app/webhooks/ultraner as your webhook URL below.

In production, just use your real domain, e.g. https://yourapp.com/webhooks/ultraner, as long as it's HTTPS and publicly reachable, no tunnel needed.

3. Register it with Ultraner

Open Console → Webhooks, click New Webhook, paste your URL, and click the events you want (they're grouped by category, no need to type event names). Save, and copy the signing secret shown, it isn't shown again, that's the value your listener above uses as ULTRANER_WEBHOOK_SECRET. Full reference: Webhooks in the API docs.

4. Test it

Use a sandbox uk_test_ API key and trigger a payment with a phone number ending in 700000001 (instant simulated success, no real gateway involved), then watch your server logs, you should see the event arrive within seconds. See sandbox mode for the full fixture list.

That's it, your server can now receive real-time events for payments, recurring subscriptions, disbursements, and more. See every real event type at Webhooks or start from a full working example at a SaaS platform integration.

Stuck? Email support@ultraner.com or ask an AI coding assistant, paste it the full docs as markdown for context.