Skip to content

Webhooks: get notified when something happens on your account

Updated November 1, 20267 min read

Add an HTTPS endpoint, choose the events it gets, verify the Nextify-Signature header, and use the delivery log to see and resend what was sent.

A webhook is a request Nextify sends to a URL of yours when something happens on your account: an instance is ready, a volume is attached, a job failed, an invoice was paid. Your scripts react to the event instead of asking the API over and over whether anything has changed.

Each event is a JSON POST to your endpoint, signed with a secret only you and Nextify know, so your endpoint can tell a real event from a forged one.

1. Add an endpoint

  1. In the portal, open Account → Webhooks.
  2. Under Add an endpoint, enter your endpoint's URL. It must:
    • start with https://,
    • use port 443 or a port from 1024 to 65535,
    • point to a public address on the internet (a private, loopback or reserved address is refused, also when a DNS name resolves to one),
    • be at most 2048 characters long, with no user name or password in it.
  3. Add a description if you like, for example "Deploy bot".
  4. Choose the events: All events (every type, including types added later) or Choose events and tick the ones you want, grouped by resource.
  5. Click Add endpoint.

The page then shows the endpoint's signing secret, which starts with whsec_. Copy it now and keep it in your application's secret store: this is the only time it is shown. If you lose it, rotate the secret (section 6) and use the new one.

An account can have up to 10 endpoints. You can do all of this with the API too: POST /v1/webhooks with an API token (see the API documentation).

2. The events

TypeWhendata.object
instance.createdAn instance was created and is readyinstance
instance.started, instance.stoppedAn instance was powered on or offinstance
instance.deletedAn instance was deletedinstance
instance.disk_resizedAn instance's root disk was growninstance
volume.created, volume.attached, volume.detached, volume.resized, volume.deletedThe volume change finishedvolume
snapshot.created, snapshot.restored, snapshot.deletedThe snapshot change finishedsnapshot
bucket.created, bucket.deletedThe bucket change finishedbucket
ip.attached, ip.detachedAn extra public IP was attached to or detached from an instancepublic IP
load_balancer.created, load_balancer.updated, load_balancer.deletedA load balancer was created, changed or deletedload balancer
job.succeededAny background job finishedjob
job.failedA background job failed for good, after its retriesjob
invoice.paidAn invoice of your account was paidinvoice
webhook.testYou clicked Send test eventa short message

webhook.test only goes to the endpoint you send it to, never to the others.

3. What your endpoint receives

POST https://hooks.example.com/nextify
Content-Type: application/json
User-Agent: Nextify-Webhooks/1.0 (+https://www.nextifynow.com/kb/webhooks)
Nextify-Event-Id: evt_4f1c9e0b7a2d4c8e9f0a1b2c3d4e5f60
Nextify-Event-Type: instance.created
Nextify-Delivery-Id: 81234
Nextify-Signature: t=1793564103,v1=5b9f0c...e41a

{"id": "evt_4f1c9e0b7a2d4c8e9f0a1b2c3d4e5f60", "type": "instance.created",
 "created": "2026-11-01T20:15:03Z", "api_version": "v1", "account_id": "...", "livemode": true,
 "data": {"object": {"id": "...", "name": "web-1", "status": "running", ...},
          "job": {"id": "48213", "type": "instance.create", "status": "succeeded", "error": null}}}
  • data.object is the resource as it was when the event happened, with the same field names you get from the API.
  • data.job is there for job events and events that a job caused.
  • Nextify-Event-Id (the same as id in the body) stays the same when a delivery is retried. Keep the ids you have handled and skip a repeat, so a retried event is not processed twice.

4. Verify the signature

Check every request before you trust it. The Nextify-Signature header looks like t=1793564103,v1=5b9f0c...:

  • t is the time the request was signed, in Unix seconds.
  • v1 is the HMAC-SHA256, in hex, of t, a dot and the raw request body, made with your endpoint's secret as the key (the whole secret, whsec_ included).

To verify:

  1. Read the request body as raw bytes. Do not parse and re-serialise the JSON first: the bytes would differ and the check would fail.
  2. Compute the HMAC-SHA256 of <t>.<raw body> with your secret and compare it with v1 using a constant-time comparison.
  3. Reject the request if t is more than 5 minutes old. This stops someone from replaying an old request. Retries are signed again with a fresh t.

Node.js (Express):

const crypto = require("node:crypto");

function verifyNextify(rawBody, header, secret) {
  const f = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
  if (!/^\d+$/.test(f.t ?? "") || Date.now() / 1000 - Number(f.t) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(f.t + ".").update(rawBody).digest();
  const given = Buffer.from(f.v1 ?? "", "hex");
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}

app.post("/nextify", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyNextify(req.body, req.get("Nextify-Signature") ?? "", process.env.NEXTIFY_WEBHOOK_SECRET)) {
    return res.sendStatus(400);
  }
  const event = JSON.parse(req.body);   // skip event.id if you have handled it already
  res.sendStatus(204);
});

Python:

import hashlib, hmac, time

def verify_nextify(raw_body: bytes, header: str, secret: str) -> bool:
    f = dict(p.strip().split("=", 1) for p in header.split(",") if "=" in p)
    t = f.get("t", "")
    if not t.isdigit() or time.time() - int(t) > 300:
        return False
    expected = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, f.get("v1", ""))

In Flask, pass request.get_data() as raw_body; in Django, request.body.

5. Answer quickly, and what happens when you do not

  • Answer with any 2xx status within 10 seconds. If handling the event takes longer, store it, answer, and do the work afterwards.
  • Any other status, a timeout, or a connection error counts as a failure. Redirects are not followed: a 3xx answer is a failure too, so give Nextify the final URL.
  • A failed delivery is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours, 5 hours and 10 hours, then every 12 hours, until 3 days after the event. After that it is marked Failed. A test event is tried only once.
  • If every delivery to an endpoint has failed for 5 days, Nextify disables the endpoint and the Webhooks page says so. Fix the receiver, then click Enable: deliveries resume.

6. Test, check the log, resend, rotate

  • Send test event sends a webhook.test event to that endpoint right away (the endpoint must be enabled). Use it after adding an endpoint to check that your receiver answers and that your signature check passes.
  • Deliveries opens the endpoint's log: every event sent in the last 30 days with its status, event type, HTTP status, number of attempts and time. Click a row to see the request body that was sent and your endpoint's answer (the first 2,000 characters). The log refreshes by itself while it is open.
  • Resend on a delivery that was delivered or has failed sends that event again at once, with the same event id and a fresh signature. Useful after you have fixed a bug in your receiver.
  • Rotate secret creates a new signing secret and shows it once. The old secret stops working immediately, so update your receiver right after rotating.
  • Disable stops deliveries to an endpoint without deleting it; Delete removes it, and deliveries still waiting for it are dropped.

Good to know

  • Each endpoint has its own secret. Never put it in code or a Git repository; keep it with your other secrets.
  • Events can arrive out of order, especially after retries. Use the created time in the body, or read the resource from the API, when order matters.
  • A read-only API token can list endpoints and read the log through the API; adding, changing, testing and resending need a full-access token or the portal.