De flesta moderna affärssystem hänger ihop. Ett betalsystem måste tala om för bokföringen att pengarna kommit in, e-handeln måste meddela lagret att en vara ska skickas, och CRM:et vill veta när en användare registrerar sig. Frågan är hur de pratar med varandra. Det enkla, men sämre, svaret är att ett system med jämna mellanrum frågar ett annat: "har det hänt något nytt?" Det smartare svaret är att systemet som vet något säger till direkt. Det är kärnan i webhooks och event-driven arkitektur.

Polling kontra webhooks: skillnaden i praktiken

Polling innebär att ditt system regelbundet anropar ett annat och frågar efter nyheter. Var trettionde sekund, varje minut, varje timme. Det är enkelt att bygga, men slösaktigt: de allra flesta anropen returnerar "inget nytt". Du betalar för trafik och kapacitet utan att få information, och du får alltid en fördröjning mellan att något händer och att du upptäcker det.

En webhook vänder på logiken. Istället för att du frågar, ringer avsändaren upp dig när något faktiskt händer. Tekniskt är en webhook bara en HTTP POST: leverantörens system skickar ett litet datapaket till en URL som du har registrerat hos dem. Stripe, GitHub, Shopify, Fortnox och tusentals andra tjänster fungerar så. Du anger en adress som https://dittforetag.se/api/webhooks/stripe, och i samma sekund som en betalning lyckas får den adressen ett meddelande.

Fördelen är realtid och effektivitet. Nackdelen är att du nu driver en offentligt nåbar endpoint som tar emot data från omvärlden - och det ställer krav som många underskattar.

Event-driven arkitektur: ett steg bortom enskilda webhooks

Webhooks är ofta första steget mot en bredare idé: event-driven arkitektur (EDA). Tankesättet är att system inte anropar varandra direkt och väntar på svar, utan publicerar händelser - "order skapad", "betalning genomförd", "kund uppsagd" - som andra system kan lyssna på och reagera på oberoende av varandra.

Det ger lös koppling: den som skapar en order behöver inte veta att det finns ett lager-, fakturerings- och mejlsystem som bryr sig. De prenumererar på händelsen var för sig. Lägger du till ett nytt system senare kopplar det bara in sig på samma ström, utan att du rör den ursprungliga koden. För ett växande företag är det skillnaden mellan en arkitektur som blir alltmer hoptrasslad och en som kan byggas ut bit för bit.

Webhooks utåt, köer inåt

I praktiken kombinerar robusta lösningar två saker. Webhooks används för att kommunicera med externa parter - din betalleverantör skickar webhooks till dig, och du skickar webhooks till exempelvis en tredjepartslogistiker. Meddelandeköer och event-bussar (som AWS SQS, RabbitMQ, Azure Event Grid eller Apache Kafka) används internt, mellan dina egna tjänster, där du vill ha kontroll, garanterad leverans och möjlighet att spela upp händelser igen.

Ett typiskt flöde: en betalning genererar en intern händelse, händelsen läggs på en kö, bakgrundsprocesser plockar upp den och bokför samt skickar kvitto, och först därefter går en webhook ut till ett externt system. Webhooks där det möter omvärlden, köer där du behöver pålitlighet.

Köer: varför du nästan alltid vill ha en

Ett vanligt nybörjarfel är att låta webhook-endpointen göra allt arbete på en gång: ta emot anropet, uppdatera databasen, skicka mejl, anropa tre andra API:er - och först därefter svara avsändaren. Det är skört. Om något av stegen är långsamt eller fallerar hinner avsändaren ge upp.

Stripe, till exempel, väntar i upp till tio sekunder på ett svar i 2xx-serien. Svarar du inte i tid räknas leveransen som misslyckad även om din kod till slut blir klar. Lösningen är enkel och kraftfull: ta emot, verifiera, lägg på kö, svara 200 - och gör det tunga arbetet asynkront. Endpointen blir snabb och svaret kommer alltid i tid. Misslyckas ett senare steg ligger händelsen kvar i kön och kan köras om utan att avsändaren behöver göra något.

Köer ger dig också utjämning av belastning. Får du tusen händelser på en sekund behöver du inte bearbeta tusen saker samtidigt - de ligger i kö och betas av i den takt dina arbetare orkar. För större volymer och flera samtidiga konsumenter blir en logg-baserad plattform som Kafka aktuell, eftersom den både skalar till mycket höga flöden och låter dig spela upp historiken på nytt.

Idempotens: regeln som räddar dig från dubbletter

Det viktigaste begreppet i hela det här fältet - och det mest förbisedda - är idempotens. En operation är idempotent om den ger samma resultat oavsett om den körs en eller flera gånger. Att "sätta saldot till 100" är idempotent. Att "lägg till 100 på saldot" är det inte.

Varför spelar det roll? Därför att webhook-leverantörer uttryckligen kan skicka samma händelse mer än en gång. Stripe är tydliga med det: vid nätverksstrul eller omförsök kan du få samma event flera gånger, och din hanterare måste klara det. Om varje mottagen "betalning genomförd" skapar en ny faktura får kunden plötsligt tre fakturor för ett köp.

Lösningen bygger på att varje händelse har ett stabilt, unikt ID som inte ändras mellan omförsök (hos Stripe börjar det med evt_). Mönstret är:

  • Läs händelsens ID när den kommer in.

  • Kolla i en databas eller cache om du redan behandlat det ID:t.

  • Har du det - svara 200 och gör inget mer.

  • Har du inte det - bearbeta händelsen och spara ID:t som behandlat.

Spara behandlade ID:n med en rimlig livslängd, ofta i storleksordningen en vecka till en månad, så att omförsök som dyker upp långt senare ändå fångas. En relaterad sak: förlita dig aldrig på att händelser kommer i rätt ordning. Stripe garanterar inte leveransordning, och det gör de flesta andra inte heller. Bygg din logik så att en "uppdaterad"-händelse som kommer före en "skapad" inte ställer till det.

Retries: planera för att det ibland går fel

Internet är opålitligt. Ditt system kommer att vara nere för underhåll, en deploy kommer att gå snett, en databas kommer att hänga sig. Bra webhook-leverantörer hanterar det med omförsök med exponentiell backoff - de försöker igen, men med allt längre mellanrum.

Stripe försöker leverera i upp till tre dygn i sitt live-läge, med ett schema som börjar direkt och sedan glesnar ut till var tolfte timme. Det betyder att även om din endpoint är nere en stund kommer händelserna fram så fort du är tillbaka. Men det fungerar bara om du följer reglerna: svara med en 2xx-kod när du tagit emot och lagt händelsen på kö, och svara med en felkod om du faktiskt inte kunde ta emot den - så att leverantören vet att försöka igen. Svarar du 200 på något du tappade är händelsen förlorad för alltid.

Tänk också på det åt andra hållet: skickar du webhooks till dina kunder eller partners behöver du själv bygga omförsök, backoff och en logg över vad som lyckats - annars blir du den opålitliga parten.

Säkerhet: din endpoint är öppen för hela internet

En webhook-URL kan i princip anropas av vem som helst som känner till den. Du måste därför kunna bevisa att ett anrop verkligen kommer från den du tror. Standarden för det är HMAC-signaturer: avsändaren räknar ut en signatur av meddelandet med en hemlig nyckel som bara ni två känner till, och skickar med den i en header. Du räknar ut samma signatur på din sida och jämför.

Några hårda regler:

  • Signera över den råa kroppen exakt som den togs emot - inte över JSON du tolkat och serialiserat om, eftersom minsta formatskillnad förstör signaturen.

  • Använd en tidssäker jämförelse av signaturer, inte vanlig stränglikhet, för att inte läcka information via svarstider.

  • Kontrollera en tidsstämpel och avvisa anrop som är för gamla, vanligen äldre än några minuter, som skydd mot att en kapad förfrågan spelas upp igen senare.

En modern trend är att de långlivade hemliga nycklarna börjar ersättas av kortlivade, automatiskt roterade signeringsnycklar. Oavsett mekanism gäller principen: lita aldrig blint på inkommande data, och behandla en webhook-endpoint med samma allvar som vilken annan publik API-yta som helst.

Standardisering: CloudEvents

Ett återkommande problem är att varje leverantör har sitt eget format på sina händelser. CloudEvents, ett projekt under Cloud Native Computing Foundation som blev en mogen "graduated"-standard i januari 2024, försöker råda bot på det genom att definiera ett gemensamt kuvert kring varje händelse - fält som id, source, type och time - oavsett vem som skickat den. Det gör dina händelser kompatibla med plattformar som Knative, Azure Event Grid och de flesta integrationsverktyg. Bygger du nytt är det värt att överväga från start.

När ska du faktiskt använda det här?

Event-driven arkitektur är kraftfullt men inte gratis - det är svårare att felsöka och resonera kring än ett rakt anrop som väntar på svar. Tumregeln:

  • Använd webhooks när du integrerar mot externa tjänster och vill reagera i realtid på saker som betalningar, ordrar eller registreringar.

  • Inför köer och event-driven arkitektur internt när du har flera tjänster som ska reagera på samma händelser, när volymerna växer, eller när du behöver lös koppling för att kunna bygga ut systemet utan att riva upp gammal kod.

  • Håll det enkelt om du har två system som pratar synkront och allt fungerar. Lägg inte till en event-buss bara för att det låter modernt.

Rätt använt är detta ryggraden i system som känns snabba, hänger ihop sömlöst och tål belastning. Fel använt blir det en källa till tappad data och svårfunna buggar - och skillnaden ligger nästan alltid i detaljerna kring köer, idempotens, retries och signaturer.

På ZORC bygger vi integrationer och event-drivna lösningar där system pratar med varandra pålitligt i realtid - med idempotens, omförsök och säker signaturhantering inbyggt från start. Vill du veta vad en integration skulle innebära för just er, beskriv projektet i vår offertkalkylator eller hör av dig via kontakt så tittar vi på det tillsammans.