För integrationer

Webhooks

Denira skickar ett anrop till en adress du väljer när någon bokar, ändrar, avbokar, betalar, får pengar tillbaka eller ställer sig på väntelistan. Adressen kan vara en webhook i Zapier eller Make, eller ditt eget system. Det ingår i alla planer.

Kom igång

  1. Gå till Inställningar › Webhooks och klicka Lägg till mottagare. Du behöver vara admin.
  2. Klistra in adressen. Den ska börja med https://.
  3. Välj händelserna mottagaren ska få och klicka Lägg till.
  4. Klicka Skicka test och välj en händelse. Mottagaren får ett exempel med påhittade uppgifter, och under Leveranser ser du vad som skickades och vad mottagaren svarade.

En anläggning kan ha upp till tio mottagare. Du kan pausa en mottagare, ändra den eller ta bort den. Platschefer ser mottagarna och leveranserna men kan inte ändra dem.

Händelser

Händelser
HändelseTypNär
Ny bokningbooking.createdEn kund bokar online eller ni bokar åt en kund.
Ändrad bokningbooking.changedTid, längd, deltagare, produkter, tillval, skydd, rabatt eller presentkort, kontaktuppgifter, personal eller status ändras.
Avbokningbooking.cancelledKunden eller ni avbokar.
Betalning mottagenpayment.receivedEn betalning registreras: kort, Swish och Klarna via Stripe, betallänk, faktura eller manuellt.
Återbetalningpayment.refundedPengar betalas tillbaka från Denira eller från Stripe, eller efter en förlorad tvist.
Ny på väntelistanwaitlist.joinedEn kund ställer sig i kö till en fullbokad tid.

booking.changed har fältet changes som säger vad som ändrades: time, duration, participants, products, addons, protections, voucher, customer, staff eller status. Status ändras när bokningen startas, avslutas eller när starten ångras. Bokningar som läses in från Bookspot skickar ingenting.

Så ser ett anrop ut

Varje händelse är ett POST-anrop med JSON. data innehåller bokningen, betalningen eller väntelisteplatsen som den var direkt efter ändringen. Vid en betalning läses bokningen en kort stund efter betalningen, så att dess betalstatus är uppdaterad. Belopp är heltal i öre: 90000 är 900 kr.

Ett anrop för en ny bokning
POST /din-adress HTTP/1.1
content-type: application/json
webhook-id: msg_k57d…
webhook-timestamp: 1780302612
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{
  "id": "msg_k57d…",
  "type": "booking.created",
  "createdAt": "2026-06-01T08:30:12.000Z",
  "test": false,
  "facility": {
    "id": "mh7c…",
    "name": "Kajakcentret"
  },
  "data": {
    "booking": {
      "id": "sample_booking",
      "bookingNumber": "2026060112345",
      "status": "confirmed",
      "paymentStatus": "paid",
      "testBooking": false,
      "createdAt": "2026-06-01T08:30:00.000Z",
      "cancelledAt": null,
      "createdBy": "customer",
      "experience": {
        "id": "sample_experience",
        "name": "Kajaktur",
        "nameEn": "Kayak tour"
      },
      "duration": {
        "id": "sample_duration",
        "name": "2 timmar",
        "nameEn": "2 hours"
      },
      "startDate": "2026-06-14",
      "startTime": "10:00",
      "endDate": "2026-06-14",
      "endTime": "12:00",
      "timeZone": "Europe/Stockholm",
      "participantCount": 2,
      "participants": [
        {
          "category": {
            "id": "sample_adult",
            "name": "Vuxen",
            "nameEn": "Adult"
          },
          "quantity": 2,
          "unitPrice": 40000
        }
      ],
      "products": [],
      "addons": [
        {
          "id": "sample_addon",
          "name": "Fika",
          "nameEn": "Coffee and bun",
          "quantity": 2,
          "unitPrice": 5000
        }
      ],
      "staff": [],
      "customer": {
        "firstName": "Anna",
        "lastName": "Andersson",
        "email": "anna@example.com",
        "phone": "+46701234567",
        "comment": "Vi kommer med cykel."
      },
      "locale": "sv",
      "totalAmount": 90000,
      "vatAmount": 18000,
      "discountAmount": 0,
      "discountCodes": [],
      "giftCardAmount": 0,
      "cancellationProtection": 5000,
      "rebookingProtection": null,
      "currency": "SEK"
    }
  }
}

Fält i varje händelse

Alla händelser
FältInnehåll
idHändelsens id, samma som huvudet webhook-id. Samma vid varje nytt försök.
typeHändelsen, till exempel booking.created.
createdAtNär det hände, ISO 8601 i UTC.
testtrue för det Skicka test skickar.
facilityAnläggningens id och namn.
databooking, payment och booking, eller waitlistEntry, beroende på händelsen. booking.changed har också changes.

Bokningen: booking

booking
FältInnehåll
id, bookingNumberBokningens id och bokningsnummer.
statuspending, confirmed, started, completed, cancelled eller no_show.
paymentStatusunpaid, partially_paid, paid, refunded eller partially_refunded.
testBookingtrue om bokningen är markerad som testbokning.
createdAt, cancelledAtNär den bokades och avbokades (null om den inte är avbokad).
createdBycustomer om kunden bokade, staff om ni bokade.
experience, durationUpplevelsen och den valda längden, med id, name och nameEn.
startDate, startTime, endDate, endTimeLokal tid i timeZone (Europe/Stockholm).
participantCount, participantsAntal deltagare, och per priskategori: category, quantity och unitPrice.
products, addonsProdukter och tillval med id, name, nameEn, quantity och unitPrice.
staffPersonal på bokningen, med id och name.
customerfirstName, lastName, email, phone och comment (högst 2 000 tecken).
localesv eller en, språket kunden bokade på.
totalAmount, vatAmountTotalpris och moms.
discountAmount, discountCodesRabatt och rabattkoderna.
giftCardAmountBelopp betalt med presentkort. Presentkortets kod skickas aldrig.
cancellationProtection, rebookingProtectionPriset för avbokningsskydd och ombokningsskydd, null om det inte valdes.
currencySEK.

Betalningen: payment

payment
FältInnehåll
id, amount, currencyBetalningens id och belopp i öre. En återbetalning är en egen rad med positivt belopp.
methodstripe, payment_link, invoice eller manual.
paidAtNär betalningen kom in (payment.received).
refundedAt, reason, sourceNär pengarna betalades tillbaka, varför, och var: denira, stripe_dashboard eller dispute (payment.refunded).

Väntelisteplatsen: waitlistEntry

waitlistEntry
FältInnehåll
id, joinedAtPlatsens id och när kunden ställde sig i kö.
experience, durationUpplevelsen och längden kunden vill ha.
date, startTime, timeZoneDen fullbokade tiden.
groupSizeHur många kunden vill boka för.
staffPersonen kunden valde, eller null.
customername, email och phone. Kunden lämnar e-post, mobilnummer eller båda.
localesv eller en.

Kontrollera signaturen

Anropen signeras enligt Standard Webhooks (standardwebhooks.com). Hemligheten (whsec_…) visas och kopieras under mottagaren i Inställningar › Webhooks. Signaturen är HMAC-SHA256 av webhook-id, webhook-timestamp och den oförändrade kroppen, ihopsatta med punkter.

Node.js med Standard Webhooks bibliotek
// Installera: npm install standardwebhooks express
import express from "express";
import { Webhook } from "standardwebhooks";

const webhook = new Webhook(process.env.DENIRA_WEBHOOK_SECRET); // whsec_… från Denira
const app = express();

app.post("/denira", express.raw({ type: "application/json" }), (req, res) => {
  let event;
  try {
    event = webhook.verify(req.body.toString(), req.headers);
  } catch {
    return res.sendStatus(400);
  }
  // event är kontrollerad. Svara snabbt och gör jobbet efteråt.
  res.sendStatus(200);
});
Samma kontroll utan bibliotek
import crypto from "node:crypto";

function isFromDenira(secret, headers, rawBody) {
  const id = headers["webhook-id"];
  const timestamp = headers["webhook-timestamp"];
  const signatures = headers["webhook-signature"];
  if (!id || !timestamp || !signatures || !/^\d+$/.test(timestamp)) return false;
  // Avvisa anrop som är äldre än fem minuter
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = Buffer.from(
    `v1,${crypto.createHmac("sha256", key).update(`${id}.${timestamp}.${rawBody}`).digest("base64")}`,
  );
  // Jämför i konstant tid, så att svarstiden inte avslöjar något
  return signatures.split(" ").some((signature) => {
    const given = Buffer.from(signature);
    return given.length === expected.length && crypto.timingSafeEqual(given, expected);
  });
}

Byter du hemlighet i Denira signeras nästa anrop med den nya direkt, så byt den hos mottagaren samtidigt.

Leverans och nya försök

  • Svara med en 2xx-status inom 15 sekunder. Allt annat räknas som misslyckat, och omdirigeringar följs inte.
  • Ett misslyckat anrop görs om efter 5 sekunder, 5 minuter, 30 minuter, 2 timmar, 5 timmar, 10 timmar och 10 timmar. Efter åtta försök markeras det som misslyckat, och du kan skicka det igen från loggen.
  • Samma händelse kan komma mer än en gång. Använd webhook-id för att hoppa över dubbletter.
  • Händelser kan komma i en annan ordning än de hände. createdAt och bokningens fält visar läget.
  • Ett test eller ett Skicka igen görs en gång. En anläggning kan göra 20 i rad och sedan ett nytt var tredje minut.
  • En anläggning kan skicka 600 händelser av varje slag i rad och sedan tio i minuten, och en händelse större än 16 kB skickas inte. Det som inte skickas syns som en varning under mottagaren.
  • Händelser och leveranser sparas i 30 dagar.

Recept: Zapier

  1. Skapa en Zap i Zapier. Välj Webhooks by Zapier som trigger och händelsen Catch Hook.
  2. Kopiera adressen Zapier visar (https://hooks.zapier.com/hooks/catch/…).
  3. Lägg till den som mottagare i Denira och välj händelser, till exempel Ny bokning.
  4. Klicka Skicka test i Denira, och sedan Test trigger i Zapier. Exemplet visas med alla fält.
  5. Lägg till nästa steg, till exempel en rad i Google Sheets eller en kontakt i Mailchimp, och välj fälten, till exempel Data Booking Customer Email.
  6. Lägg gärna ett filter före steget som bara släpper igenom Test är false, så att testerna inte hamnar i kalkylarket. Publicera sedan Zappen.

Webhooks by Zapier ingår bara i Zapiers betalda abonnemang. Zapier kontrollerar inte signaturen.

Recept: Make

  1. Skapa ett scenario i Make och lägg till modulen Webhooks › Custom webhook. Klicka Add, ge den ett namn och spara.
  2. Kopiera adressen Make visar (till exempel https://hook.eu1.make.com/…).
  3. Lägg till den som mottagare i Denira och välj händelser.
  4. Make väntar nu på ett exempel. Klicka Skicka test i Denira så lär sig Make fälten.
  5. Lägg till nästa modul och välj fälten under data, till exempel data.booking.customer.email.
  6. Lägg ett filter efter webhooken som kräver att test är false, och slå på scenariot.

Webhooks ingår i Makes gratisplan.

Kom igång i dag

Skapa kontot själv, eller låt oss sätta upp allt med Uppstartshjälp. Start kostar 0 kr i månaden.

Skapa konto

Inget kort krävs för Start. Uppstartshjälp: gratis för de 20 första företagen.

Ställ en fråga

Vi svarar inom 24 timmar.

Byter du från Bookspot?

Importen tar med upplevelser, priser och bokningar. Uppstartshjälpen är gratis för dig.

Har du frågor?

Skriv till oss, så svarar vi inom 24 timmar.