Skip to main content

Webhookit

Frihetin webhookit mahdollistavat HTTP-ilmoitusten vastaanottamisen reaaliaikaisesti, kun tililläsi tapahtuu tapahtumia. API:n pollaamisen sijaan määritä URL ja Frihet lähettää POST-pyynnön tapahtuman tiedoilla heti, kun se tapahtuu.

Konfigurointi

Voit hallita webhookkeja Frihetin hallintapaneelista (Asetukset > Webhookit) tai ohjelmallisesti REST API:n kautta.

Hallintapaneelista

  1. Siirry kohtaan Asetukset > Webhookit Frihet-tililläsi
  2. Napsauta Luo webhook
  3. Syötä kuvaava nimi, kohde-URL ja valitse tapahtumat, jotka haluat vastaanottaa
  4. Valinnaisesti, määritä salaisuus HMAC-vahvistusta varten (suositellaan)
  5. Tallenna webhook

Webhookien REST API

Hallitse webhookkeja ohjelmallisesti täydellisten CRUD-rajapintojen avulla.

Listaa webhookit

GET /v1/webhooks
curl https://api.frihet.io/v1/webhooks \
-H "X-API-Key: fri_tu-clave-aqui"

Vastaus (200):

{
"data": [
{
"id": "wh_abc123",
"url": "https://mi-app.com/hook",
"events": ["invoice.paid", "invoice.created"],
"active": true,
"createdAt": "2026-02-01T10:00:00.000Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}

Hae webhook

GET /v1/webhooks/:id
curl https://api.frihet.io/v1/webhooks/wh_abc123 \
-H "X-API-Key: fri_tu-clave-aqui"

Luo webhook

POST /v1/webhooks
KenttäTyyppiPakollinenKuvaus
urlstringKylläKohde-URL (HTTPS pakollinen tuotannossa)
eventsstring[]KylläLista vastaanotettavista tapahtumatyypeistä
secretstringEiSalaisuus HMAC-SHA256 allekirjoitusta varten (suositellaan)
activebooleanEiWebhookin tila (oletus: true)
curl -X POST https://api.frihet.io/v1/webhooks \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"url": "https://mi-app.com/hook",
"events": ["invoice.paid", "expense.created"],
"secret": "mi-secreto-hmac"
}'

Vastaus (201):

{
"id": "wh_def456",
"url": "https://mi-app.com/hook",
"events": ["invoice.paid", "expense.created"],
"active": true,
"createdAt": "2026-03-18T10:00:00.000Z"
}

Päivitä webhook

PATCH /v1/webhooks/:id

Sinun tarvitsee lähettää vain kentät, jotka haluat muokata.

curl -X PATCH https://api.frihet.io/v1/webhooks/wh_def456 \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"events": ["invoice.paid", "invoice.created", "expense.created"],
"active": false
}'

Vastaus (200): Päivitetty webhook-objekti.

Poista webhook

DELETE /v1/webhooks/:id
curl -X DELETE https://api.frihet.io/v1/webhooks/wh_def456 \
-H "X-API-Key: fri_tu-clave-aqui"

Vastaus: 204 No Content


URL:n vaatimukset:

  • On käytettävä HTTPS-protokollaa (HTTP sallittu vain localhost- ja 127.0.0.1-osoitteille kehityksen aikana)
  • Yksityiset IP-osoitteet (10.x, 172.16-31.x, 192.168.x) eivät ole sallittuja SSRF-hyökkäysten estämiseksi
  • Sen on vastattava 2xx-koodilla alle 30 sekunnissa

Rajoitukset:

  • Enintään 20 webhookia per tili
  • Enintään 100 KB payload per toimitus

Tapahtumatyypit

Frihet lähettää 14 tapahtumatyyppiä, jotka on ryhmitelty resurssin mukaan.

Laskut (4 tapahtumaa)

TapahtumaKuvaus
invoice.createdUusi lasku on luotu
invoice.updatedOlemassa olevaa laskua on muokattu
invoice.paidLasku on merkitty maksetuksi
invoice.overdueLasku on erääntynyt

Kulut (2 tapahtumaa)

TapahtumaKuvaus
expense.createdUusi kulu on rekisteröity
expense.updatedOlemassa olevaa kulua on muokattu

Tarjoukset (4 tapahtumaa)

TapahtumaKuvaus
quote.createdUusi tarjous on luotu
quote.updatedOlemassa olevaa tarjousta on muokattu
quote.acceptedAsiakas on hyväksynyt tarjouksen (tilan muutos tilaan accepted)
quote.rejectedAsiakas on hylännyt tarjouksen (tilan muutos tilaan rejected)

Asiakkaat (2 tapahtumaa)

TapahtumaKuvaus
client.createdUusi asiakas on rekisteröity
client.updatedAsiakkaan tietoja on muokattu

Tuotteet (2 tapahtumaa)

TapahtumaKuvaus
product.createdUusi tuote tai palvelu on luotu
product.updatedOlemassa olevaa tuotetta tai palvelua on muokattu

Payloadin rakenne

Jokainen webhook-toimitus on POST-pyyntö JSON-rungolla. Rakenne on sama kaikille tapahtumatyypeille:

{
"event": "invoice.paid",
"timestamp": "2026-02-12T14:30:00.000Z",
"data": {
"id": "inv_abc123",
"clientName": "Acme S.L.",
"items": [
{ "description": "Consultoria", "quantity": 10, "unitPrice": 75 }
],
"total": 750,
"status": "paid",
"paidAt": "2026-02-12T14:29:58.000Z",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-02-12T14:30:00.000Z"
}
}

data-kenttä sisältää resurssin täydellisen tilan tapahtumahetkellä.


Toimitusotsakkeet

Jokainen webhook-pyyntö sisältää seuraavat otsakkeet:

OtsakeKuvausEsimerkki
Content-TypeSisällön tyyppiapplication/json
X-Frihet-EventTapahtuman tyyppiinvoice.paid
X-Frihet-Delivery-IdToimituksen yksilöllinen tunnisted4e5f6a7b8c9
X-Frihet-TimestampISO 8601 aikaleima2026-02-12T14:30:00.000Z
X-Frihet-SignaturePayloadin HMAC-SHA256 allekirjoitussha256=a1b2c3d4e5f6...

X-Frihet-Signature-otsake sisältyy vain, jos olet määrittänyt salaisuuden webhookille.


Allekirjoituksen vahvistaminen

Jos määrität salaisuuden luodessasi webhookin, Frihet allekirjoittaa jokaisen payloadin HMAC-SHA256:lla. Vahvistusprosessi sisältää:

  1. Pyynnön raakarungon ottaminen
  2. HMAC-SHA256:n laskeminen käyttäen salaisuutta avaimena
  3. Tuloksen vertaaminen X-Frihet-Signature-otsakkeen arvoon (ilman sha256=-etuliitettä)

SDK:n avulla (suositellaan)

import { Webhooks } from '@frihet/sdk';

app.post('/webhook/frihet', express.raw({ type: 'application/json' }), (req, res) => {
const isValid = Webhooks.verifySignature(
req.body,
req.headers['x-frihet-signature'],
process.env.FRIHET_WEBHOOK_SECRET,
);

if (!isValid) return res.status(401).send('Virheellinen allekirjoitus');

const event = JSON.parse(req.body.toString());
console.log(req.headers['x-frihet-event'], event.data);
res.sendStatus(200);
});

Manuaalinen esimerkki Node.js:ssä

const crypto = require('crypto');

function verifyWebhookSignature(rawBody, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');

const receivedSignature = signature.replace('sha256=', '');

return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}

// Käyttö Express-palvelimella
app.post('/webhook/frihet', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-frihet-signature'];
const secret = process.env.FRIHET_WEBHOOK_SECRET;

if (!signature || !verifyWebhookSignature(req.body.toString(), signature, secret)) {
return res.status(401).send('Virheellinen allekirjoitus');
}

const event = JSON.parse(req.body.toString());
const eventType = req.headers['x-frihet-event'];

console.log(`Vastaanotettu tapahtuma: ${eventType}`, event.data);

// Käsittele tapahtuma tyypin mukaan
switch (eventType) {
case 'invoice.paid':
// Päivitä kirjanpitojärjestelmäsi
break;
case 'expense.created':
// Ilmoita talousryhmälle
break;
}

res.status(200).send('OK');
});

Esimerkki Pythonissa

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = 'tu-secreto-aqui'

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()

received = signature.replace('sha256=', '')

return hmac.compare_digest(expected, received)

@app.route('/webhook/frihet', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Frihet-Signature', '')
if not verify_signature(request.data, signature, WEBHOOK_SECRET):
abort(401)

event = request.get_json()
event_type = request.headers.get('X-Frihet-Event')

print(f'Vastaanotettu tapahtuma: {event_type}')

if event_type == 'invoice.paid':
# Päivitä kirjanpitojärjestelmäsi
pass
elif event_type == 'quote.accepted':
# Muunna tarjous laskuksi
pass

return 'OK', 200

Tärkeää: Käytä aina vakioaikakomparointia (timingSafeEqual Node.js:ssä, compare_digest Pythonissa) ajoitushyökkäysten estämiseksi.


Uudelleenyrityskäytäntö

Jos webhookin toimitus epäonnistuu (ei-2xx-vastakoodi tai aikakatkaisu), Frihet yrittää automaattisesti uudelleen eksponentiaalisen viiveen kanssa:

YritysViiveKumulatiivinen aika
1 (alkuperäinen)välittömästi0s
2 (ensimmäinen uudelleenyritys)2 sekuntia2s
3 (toinen uudelleenyritys)4 sekuntia6s
  • Enintään 3 yritystä per toimitus (1 alkuperäinen + 2 uudelleenyritystä)
  • Enintään 30 sekunnin viive uudelleenyritysten välillä
  • 30 sekunnin aikakatkaisu per pyyntö
  • Jos kaikki 3 yritystä epäonnistuvat, toimitus merkitään tilaksi failed

Uudelleenyritykset käsitellään tehtävällä, joka suoritetaan 5 minuutin välein, mikä varmistaa luotettavuuden, vaikka pääprosessi käynnistettäisiin uudelleen.

Voit tarkastella kunkin webhookin toimitushistoriaa Frihet-paneelista, mukaan lukien vastakoodi, vastauksen runko (ensimmäiset 1000 merkkiä) ja kunkin yrityksen virheet.


Testaus

Frihet-paneelista voit lähettää testitapahtuman mille tahansa konfiguroidulle webhookille. Testipayloadilla on seuraava rakenne:

{
"event": "webhook.test",
"timestamp": "2026-02-12T14:30:00.000Z",
"data": {
"message": "This is a test webhook from Frihet ERP"
}
}

Tämä mahdollistaa sen varmistamisen, että rajapisteesi on käytettävissä, että allekirjoitus vahvistetaan oikein ja että järjestelmäsi käsittelee tapahtumat virheettömästi.


Hyvät käytännöt

Vastaa nopeasti

Rajapisteesi tulisi vastata 200-koodilla mahdollisimman pian. Jos sinun on suoritettava raskasta käsittelyä (sähköpostien lähettäminen, ulkoisten tietokantojen päivittäminen jne.), hyväksy tapahtuma ja käsittele se asynkronisesti työjonossa.

Hallitse idempotenttia

On mahdollista, että sama tapahtuma toimitetaan useammin kuin kerran (esimerkiksi jos palvelimesi vastasi aikakatkaisulla, mutta käsitteli tapahtuman). Käytä X-Frihet-Delivery-Id-kenttää idempotenttiavainena kaksoiskappaleiden välttämiseksi.

Vahvista allekirjoitus aina

Älä koskaan luota webhookiin tarkistamatta X-Frihet-Signature-otsaketta. Kuka tahansa toimija, jolla on pääsy URL-osoitteeseesi, voi lähettää väärennettyjä payloadeja.

Käytä HTTPS:ää

Tuotannossa rajapisteesi tulee suojata HTTPS:llä. Frihet hylkää HTTP-URL-osoitteet (paitsi paikallisessa kehityksessä).

Valvo vikoja

Tarkista säännöllisesti Frihet-paneelin toimituslokeja. Jos näet toistuvasti epäonnistuneita toimituksia, varmista, että rajapisteesi on käytettävissä ja vastaa alle 30 sekunnissa.

Suodata tapahtumat

Tilaa vain tarvitsemasi tapahtumat. Jokainen webhook voi kuunnella yhtä tai useampaa tapahtumatyyppiä. Mitä vähemmän turhia tapahtumia käsittelet, sitä pienempi kuormitus palvelimellasi on.


Toimitusten virheenkorjaus

Frihet tallentaa jokaisen webhook-toimituksen tuloksen. Voit tarkastella sitä paneelista:

  1. Siirry kohtaan Asetukset > Kehittäjät > Webhookit
  2. Napsauta webhookia, jota haluat tarkastella
  3. Avaa Toimitukset-välilehti

Jokainen merkintä näyttää:

  • Rajapisteesi palauttaman HTTP-vastakoodin
  • Vasteajan millisekunteina
  • Vastauksen rungon (ensimmäiset 1000 merkkiä)
  • Jokaisen yrityksen päivämäärän ja kellonajan (mukaan lukien uudelleenyritykset)
  • Lopullisen tilan: delivered, retrying tai failed

Yleisiä ongelmia

OireTodennäköinen syyRatkaisu
Kaikki toimitukset epäonnistuvat aikakatkaisullaRajapiste vastaa yli 30 sekunnissaHyväksy tapahtuma välittömällä 200-koodilla ja käsittele asynkronisesti
SSL/TLS-virheVanhentunut sertifikaatti tai epätäydellinen ketjuUusi sertifikaatti ja tarkista ketju openssl s_client:llä
Järjestelmällinen 403-koodiPalomuuri estää saapuvat POST-pyynnötSalli liikenne Google Cloudin IP-osoitteista (us-central1)
Koodi 502/503Palvelin kaatunut tai huollossaTarkista palvelimesi lokit ja varmista, että prosessi on aktiivinen

Testipainike

Kunkin webhookin tiedot-näytössä Lähetä testi -painike lähettää synteettisen webhook.test-tapahtuman URL-osoitteeseesi. Käytä tätä painiketta tarkistaaksesi:

  • Että URL-osoite on käytettävissä internetistä
  • Että HMAC-allekirjoitus vahvistetaan oikein
  • Että palvelimesi vastaa 2xx-koodilla

Testin tulos ilmestyy välittömästi toimituslokiiin.


Vianmääritys

En vastaanota webhookeja

  1. Varmista, että webhookin URL on oikea ja käytettävissä internetistä
  2. Tarkista, että webhook on aktiivisessa tilassa paneelissa
  3. Tarkista toimituslokit virheiden varalta
  4. Jos käytät palomuuria, varmista, että saapuvat POST-pyynnöt Google Cloudista (us-central1) ovat sallittuja

Allekirjoitukset eivät täsmää

  1. Varmista, että käytät pyynnön raakaa runkoa (ennen JSON-parsintaa)
  2. Vahvista, että koodisi salaisuus vastaa Frihet-paneelissa määritettyä
  3. Älä muokkaa tai uudelleenmuotoile runkoa ennen allekirjoituksen tarkistamista
  4. Tarkista, ettei kehysjärjestelmäsi jäsenna runkoa automaattisesti ennen kuin pääset käsiksi raakaan

Uudelleenyritykset eivät saavu

  1. Uudelleenyritykset käsitellään 5 minuutin välein. Jos rajapisteesi oli lyhyesti kaatunut, yritykset saattavat olla jo loppuun käytettyjä
  2. Tarkista toimitushistoria varmistaaksesi kunkin yrityksen tilan
  3. Jos kaikki 3 yritystä epäonnistuivat, toimitusta ei yritetä uudelleen automaattisesti

Payload liian suuri

Jos tapahtumaan liittyvä resurssi on erittäin suuri (monet rivit laskussa, laajat kentät), payload voi ylittää 100 KB:n rajan ja toimitus hylätään. Yksinkertaista resurssin tietoja tai ota yhteyttä tukeen, jos tarvitset suurempaa rajaa.