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
- Siirry kohtaan Asetukset > Webhookit Frihet-tililläsi
- Napsauta Luo webhook
- Syötä kuvaava nimi, kohde-URL ja valitse tapahtumat, jotka haluat vastaanottaa
- Valinnaisesti, määritä salaisuus HMAC-vahvistusta varten (suositellaan)
- 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ä | Tyyppi | Pakollinen | Kuvaus |
|---|---|---|---|
url | string | Kyllä | Kohde-URL (HTTPS pakollinen tuotannossa) |
events | string[] | Kyllä | Lista vastaanotettavista tapahtumatyypeistä |
secret | string | Ei | Salaisuus HMAC-SHA256 allekirjoitusta varten (suositellaan) |
active | boolean | Ei | Webhookin 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- ja127.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)
| Tapahtuma | Kuvaus |
|---|---|
invoice.created | Uusi lasku on luotu |
invoice.updated | Olemassa olevaa laskua on muokattu |
invoice.paid | Lasku on merkitty maksetuksi |
invoice.overdue | Lasku on erääntynyt |
Kulut (2 tapahtumaa)
| Tapahtuma | Kuvaus |
|---|---|
expense.created | Uusi kulu on rekisteröity |
expense.updated | Olemassa olevaa kulua on muokattu |
Tarjoukset (4 tapahtumaa)
| Tapahtuma | Kuvaus |
|---|---|
quote.created | Uusi tarjous on luotu |
quote.updated | Olemassa olevaa tarjousta on muokattu |
quote.accepted | Asiakas on hyväksynyt tarjouksen (tilan muutos tilaan accepted) |
quote.rejected | Asiakas on hylännyt tarjouksen (tilan muutos tilaan rejected) |
Asiakkaat (2 tapahtumaa)
| Tapahtuma | Kuvaus |
|---|---|
client.created | Uusi asiakas on rekisteröity |
client.updated | Asiakkaan tietoja on muokattu |
Tuotteet (2 tapahtumaa)
| Tapahtuma | Kuvaus |
|---|---|
product.created | Uusi tuote tai palvelu on luotu |
product.updated | Olemassa 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:
| Otsake | Kuvaus | Esimerkki |
|---|---|---|
Content-Type | Sisällön tyyppi | application/json |
X-Frihet-Event | Tapahtuman tyyppi | invoice.paid |
X-Frihet-Delivery-Id | Toimituksen yksilöllinen tunniste | d4e5f6a7b8c9 |
X-Frihet-Timestamp | ISO 8601 aikaleima | 2026-02-12T14:30:00.000Z |
X-Frihet-Signature | Payloadin HMAC-SHA256 allekirjoitus | sha256=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ää:
- Pyynnön raakarungon ottaminen
- HMAC-SHA256:n laskeminen käyttäen salaisuutta avaimena
- Tuloksen vertaaminen
X-Frihet-Signature-otsakkeen arvoon (ilmansha256=-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:
| Yritys | Viive | Kumulatiivinen aika |
|---|---|---|
| 1 (alkuperäinen) | välittömästi | 0s |
| 2 (ensimmäinen uudelleenyritys) | 2 sekuntia | 2s |
| 3 (toinen uudelleenyritys) | 4 sekuntia | 6s |
- 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:
- Siirry kohtaan Asetukset > Kehittäjät > Webhookit
- Napsauta webhookia, jota haluat tarkastella
- 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,retryingtaifailed
Yleisiä ongelmia
| Oire | Todennäköinen syy | Ratkaisu |
|---|---|---|
| Kaikki toimitukset epäonnistuvat aikakatkaisulla | Rajapiste vastaa yli 30 sekunnissa | Hyväksy tapahtuma välittömällä 200-koodilla ja käsittele asynkronisesti |
| SSL/TLS-virhe | Vanhentunut sertifikaatti tai epätäydellinen ketju | Uusi sertifikaatti ja tarkista ketju openssl s_client:llä |
| Järjestelmällinen 403-koodi | Palomuuri estää saapuvat POST-pyynnöt | Salli liikenne Google Cloudin IP-osoitteista (us-central1) |
| Koodi 502/503 | Palvelin kaatunut tai huollossa | Tarkista 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
- Varmista, että webhookin URL on oikea ja käytettävissä internetistä
- Tarkista, että webhook on aktiivisessa tilassa paneelissa
- Tarkista toimituslokit virheiden varalta
- Jos käytät palomuuria, varmista, että saapuvat POST-pyynnöt Google Cloudista (
us-central1) ovat sallittuja
Allekirjoitukset eivät täsmää
- Varmista, että käytät pyynnön raakaa runkoa (ennen JSON-parsintaa)
- Vahvista, että koodisi salaisuus vastaa Frihet-paneelissa määritettyä
- Älä muokkaa tai uudelleenmuotoile runkoa ennen allekirjoituksen tarkistamista
- Tarkista, ettei kehysjärjestelmäsi jäsenna runkoa automaattisesti ennen kuin pääset käsiksi raakaan
Uudelleenyritykset eivät saavu
- Uudelleenyritykset käsitellään 5 minuutin välein. Jos rajapisteesi oli lyhyesti kaatunut, yritykset saattavat olla jo loppuun käytettyjä
- Tarkista toimitushistoria varmistaaksesi kunkin yrityksen tilan
- 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.