Webhooks
Last updated: April 22, 2026
För att använda webhooks krävs att organisationen har produkten "Webhooks" samt att du som användare har rollen "Kan hantera webhooks". För hjälp med att sätta upp detta kontakta Bemlo.
Användning
Webhooks används för att få Bemlo att i realtid skicka händelser till en URL i ditt eget system. Varje gång en händelse du prenumererar på inträffar gör Bemlo en signerad POST-förfrågan till din URL med händelsen som JSON.
Typiska användningsområden:
Skicka avrop vidare till ert kvalitetsuppföljnings- eller CRM-system när de publiceras.
Trigga interna notifieringar när ett avrop stängs, uppdateras eller markeras som brådskande.
Hålla en extern datastore i synk utan att behöva polla Bemlos API.
Vilka händelser som är tillgängliga beror på vilka produkter er organisation har. Organisationer med "Skapa avrop" kan prenumerera på både publicerade avrop och avropsutkast. Organisationer med "Presentera kandidater" kan prenumerera på publicerade avrop för att få notiser om tillgängliga uppdrag.
Hantera webhooks
Användare med rollen "Kan hantera webhooks" kan skapa och redigera webhooks via Inställningar → Webhooks.
Skapa webhook
När du skapar en webhook behöver du fylla i tre fält:
URL – den endpoint i ditt system som Bemlo skickar händelser till. Måste börja med
https://i produktion.Beskrivning – visas i listan över webhooks och hjälper dig hålla ordning på vad varje endpoint används till.
Händelser – kryssa i de händelser du vill prenumerera på. Endast händelser som er organisation är berättigad till visas.
Direkt efter att webhooken skapats visas en hemlighet (ett värde med prefixet whsec_). Den används för att signera alla framtida leveranser så att ditt system kan verifiera att förfrågan kommer från Bemlo. Hemligheten visas endast direkt efter skapandet, så se till att kopiera och spara den på ett säkert ställe. Om du tappar bort den måste du rotera hemligheten (se nedan).
Uppdatera webhook
Genom att klicka på en webhook i listan öppnas en sidomeny där du kan:
Ändra URL, beskrivning och prenumererade händelser.
Aktivera eller inaktivera webhooken. En inaktiverad webhook tar inte emot några leveranser.
Se om det finns misslyckade leveranser i rad (efter 5 misslyckanden i rad inaktiveras webhooken automatiskt — mer om det under Leveransgarantier).
Rotera hemlighet
Om hemligheten har läckt eller tappats bort kan du rotera den från sidomenyn. Den gamla hemligheten ogiltigförklaras omedelbart och en ny visas en gång. Glöm inte att uppdatera verifieringen i ditt system innan du roterar.
Radera webhook
Att radera en webhook tar bort den permanent tillsammans med hela leveranshistoriken. Vill du pausa leveranserna tillfälligt är det bättre att inaktivera webhooken istället.
Mottagen händelse
Bemlo skickar en POST-förfrågan med Content-Type: application/json till din URL. Förfrågan innehåller följande headers:
Header | Beskrivning |
| Unikt ID för leveransen (samma vid retries). |
| Unix-tid (sekunder) då leveransen signerades. |
| HMAC-SHA256-signatur på formen |
Bodyn är ett JSON-objekt:
{
"id": "01HAY…",
"type": "TENDER.PUBLISHED",
"timestamp": 1714560000,
"data":
{
"tenderId": "…",
"title": "Sjuksköterska, vårdcentral",
"profession": "NURSE",
"unitName": "Vårdcentral Alfa",
"municipality": "Stockholm",
"startsAt": "2026-05-01T07:00:00.000Z",
"endsAt": "2026-07-31T15:00:00.000Z",
"lastPresentationDate": null
}
}Fältet type har formen SCOPE.ACTION (t.ex. TENDER.PUBLISHED, TENDER.CLOSED, DRAFT_TENDER.MARKED_AS_READY_TO_PUBLISH).
Svara med HTTP 2xx inom 5 sekunder för att Bemlo ska betrakta leveransen som lyckad. Allt annat räknas som ett misslyckande och försöks igen (se Leveransgarantier).
Verifiera signaturen (rekommenderas)
Headern X-Webhook-Signature är valfri att verifiera, men vi rekommenderar starkt att alla mottagare gör det i produktion. Utan verifiering kan vem som helst som känner till din URL skicka falska händelser till ditt system.
Så fungerar signaturen
För varje leverans beräknar Bemlo:
signature = HMAC-SHA256(secret, ${timestamp}.${body})Där:
secretär din webhook-hemlighet (värdet med prefixwhsec_).timestampär samma värde som skickas iX-Webhook-Timestamp.bodyär den råa request-bodyn, byte för byte. Parsa inte JSON först och serialisera om — det ändrar white-space och ogiltigförklarar signaturen.
Signaturen skickas som X-Webhook-Signature: sha256=<hex>.
Steg för att verifiera
1. Läs ut X-Webhook-Timestamp och X-Webhook-Signature från headern.
2. Läs request-bodyn som en rå sträng (inte parsad JSON).
3. Beräkna HMAC-SHA256(secret, "<timestamp>.<body>") och jämför i konstant tid med signaturen efter sha256=.
4. Kontrollera att timestamp inte är för gammal — vi rekommenderar att avvisa leveranser äldre än 5 minuter för att skydda mot replay-attacker.
Exempel i Node.js (Express)
import crypto from 'node:crypto'
import express from 'express'
const app = express()
const WEBHOOK_SECRET = process.env.BEMLO_WEBHOOK_SECRET
// Viktigt: använd raw body, inte express.json(), för signaturverifieringen.
app.post(
'/bemlo/webhook',
express.raw({ type: 'application/json' }),
(req, res) => {
const timestamp = req.header('x-webhook-timestamp')
const signatureHeader = req.header('x-webhook-signature') ?? ''
const providedSignature = signatureHeader.replace(/^sha256=/, '')
const body = req.body.toString('utf8')
// Avvisa gamla leveranser (skydd mot replay).
const ageSeconds = Math.floor(Date.now() / 1000) - Number(timestamp)
if (!timestamp || Math.abs(ageSeconds) > 300) {
return res.status(400).send('stale timestamp')
}
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(`${timestamp}.${body}`)
.digest('hex')
const valid =
providedSignature.length === expectedSignature.length &&
crypto.timingSafeEqual(
Buffer.from(providedSignature, 'hex'),
Buffer.from(expectedSignature, 'hex'),
)
if (!valid) return res.status(401).send('invalid signature')
const event = JSON.parse(body)
// Hantera event.type och event.data …
res.status(200).send('ok')
},
)Exempel i Python (Flask)
import hmac, hashlib, os, time
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = os.environ["BEMLO_WEBHOOK_SECRET"].encode()
@app.post("/bemlo/webhook")
def bemlo_webhook():
timestamp = request.headers.get("X-Webhook-Timestamp", "")
signature_header = request.headers.get("X-Webhook-Signature", "")
provided = signature_header.removeprefix("sha256=")
body = request.get_data() # råa bytes — viktigt
try:
age = abs(int(time.time()) - int(timestamp))
except ValueError:
abort(400)
if age > 300:
abort(400)
expected = hmac.new(
WEBHOOK_SECRET,
f"{timestamp}.".encode() + body,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(provided, expected):
abort(401)
event = request.get_json()
# Hantera event["type"] och event["data"] …
return "", 200Leveransgarantier
Bemlo försöker leverera en händelse i upp till 6 försök med följande backoff: direkt, efter 30 s, 2 min, 10 min, 30 min och slutligen 2 timmar. Därefter markeras leveransen som
DEADoch försöks inte igen.Om 5 leveranser i rad misslyckas inaktiveras webhooken automatiskt. Du kan återaktivera den från inställningssidan när endpointen fungerar igen — misslyckande-räknaren nollställs när webhooken aktiveras på nytt.
Leveranser kan i sällsynta fall skickas mer än en gång (t.ex. om ditt system svarar långsamt och vår retry-timer löper ut). Använd
X-Webhook-Idför att idempotent bearbeta samma händelse två gånger.Ordningen på leveranser är inte garanterad. Om ordning är viktig, använd
timestampi bodyn tillsammans med egen logik.
Testa webhook
Vi rekommenderar att verifiera uppsättningen mot en tjänst som webhook.site innan du kopplar in din produktionsmottagare. Skapa en webhook som pekar på den URL:en, utlös en testhändelse (t.ex. publicera ett avrop), och verifiera att headers, body och signatur ser ut som förväntat.