Siirry pääsisältöön
Certyneo
Kehittäjädokumentti

Webhooks vastaanottaa allekirjoitustapahtumat reaaliajassa

Määritä HTTPS-URL Certyneo-hallintapaneelissasi ja vastaanota HMAC-SHA256-allekirjoitettu POST aina, kun kirjekuorissasi tapahtuu jotakin: allekirjoitus, kieltäytyminen, vanhentuminen. 11 tuettua tapahtumaa, 5 toimintoyritystä eksponentiaalisella viiveellä, salausvarmennus 8 koodirivillä.

< 5s

Tapahtuman jälkeinen keskimääräinen toimitusaika

5x

Toimintoyritykset yhteensä, jaettu noin 1 h 20 min ajalle

HMAC-SHA256

Jokaisen pyynnön allekirjoitusalgoritmi

Tapahtumakartta

12 alla olevaa tapahtumaa kattaa Certyneo-kirjekuoren koko elinkaaren. Ota käyttöön ne, jotka kiinnostavat sinua kohdassa Asetukset → Webhookit, jätä muut huomioimatta — tilaus on jäsennelty tapahtumittain.

TapahtumaSe on aiheutunut
envelope.createdKirjaimessa on luotu kuori (UI, API tai malli) hyödyllinen CRM-sivuston synkronoimiseksi heti luodussa.
envelope.sentKirjaimeksianto lähetetään allekirjoittajalle (ensimmäinen lähetetty sähköposti).
envelope.completedKaikki allekirjoittajat ovat allekirjoittaneet ja eIDAS-sinetöity PDF on tallennettu. Payload sisältää signedDocumentUrl-linkin, ennakkoon allekirjoitetun linkin, joka on voimassa 7 päivää; muussa tapauksessa käytä GET /v1/envelopes/id/signed-document ja tarkastuspolku GET /v1/envelopes/id/audit-trail-kautta.
envelope.declinedAllekirjoittaja kieltäytyi kirjekuoresta. Kieltäytyjän osoite on kohdassa `data.declinedBy` ja perustelu, jos se on syötetty, kohdassa `data.reason`.
envelope.voidedKirjaimeksiantaja peruutti kirjekuoren ennen kuin se oli allekirjoitettu.
envelope.expiredKirjekuoren vanhentumispäivä on ohitettu ilman täydellistä allekirjoitusta. Kysy GET /v1/envelopes/id nähdäksesi puuttuvat allekirjoittajat.
envelope.returned_to_senderAllekirjoittaja palautti kirjekuoren lähettäjälle korjausta varten ilman sen kieltäytymistä. Perustelu on kohdassa `data.reason` ja palauttajan tekijä kohdassa `data.returnedBy`.
envelope.resubmittedLähettäjä korjasi ja palautti aiemmin palautetun kirjekuoren. Merkitsee allekirjoituskierron jatkamista.
recipient.signedYksittäinen allekirjoittaja on allekirjoittanut (mutta ei välttämättä kaikki). Hyödyllinen edistymisen seurantaan ja seuraavan vaiheen ketjuttamiseen peräkkäisessä työnkulussa. Varoitus: allekirjoitettua PDF:ää ei ole vielä olemassa tässä vaiheessa, jopa viimeiselle allekirjoittajalle — täältä käynnistetty lataus palauttaa HTTP 409:n. Käytä envelope.completed-tapahtumaa asiakirjaa varten.
recipient.viewedAllekirjoittaja avasi allekirjoituslinkin allekirjoittamatta vielä.
recipient.approvedHyväksyjä hyväksyi kirjekuoren ilman allekirjoituksen liittämistä (sisäisen vahvistuksen työnkulku). Osoite on kohdassa `data.approvedBy`.
recipient.bouncedVastaanottajan sähköpostipalvelin hylkäsi kutsu tai uudelleenyrityksen lopullisesti (olematon postilaatikko, kuollut verkkotunnus). Vastaanottaja siirtyy BOUNCED-tilaan ja automaattiset uudelleenyritykset pysähtyvät. Osoite on kohdassa `data.recipientEmail`: korjaa se ja lähetä kirjekuori uudelleen.

recipient.signed ei tarkoita « asiakirja saatavilla »

recipient.signed lähetetään JOKAISELLE allekirjoittajalle, kun hän lopettaa allekirjoituksensa — myös viimeiselle, ennen kuin sinetöity PDF on koottu ja tallennettu. Tältä käsittelijältä käynnistetty lataus saa siis aina HTTP 409:n « Signed document not available until the envelope is COMPLETED ». Tämä ei ole virhe: se on « ei vielä valmis ». Tilaa envelope.completed asiakirjan hakemiseksi ja pidä recipient.signed edistymisen seurantaan (kuka allekirjoitti ja milloin).

Palveluvaraston muoto

Kaikilla toiminnoilla on sama ylin JSON-kaavio: `event`, `data` ja `timestamp`. Tapahtuman nimi toistetaan myös `X-Certyneo-Event`-otsikossa, mikä mahdollistaa reitityksen jo ennen tekstin jäsentämistä. `data`-sisältö vaihtelee tapahtuman mukaan, mutta on aina litteä objekti yksinkertaisista arvoista — ei koskaan taulukkoa eikä sisäkkäistä objektia. Tässä on täydellinen `envelope.completed`-toiminto.

POST /webhooks/certyneo HTTP/1.1
Host: your.app
Content-Type: application/json
X-Certyneo-Event: envelope.completed
X-Certyneo-Delivery-Id: cm8f2h6a10004qr9k5p2wm3xt
X-Certyneo-Signature: 4f3d1c8b2a9e7f60d5c4b3a2918e7f6d5c4b3a2918e7f6d5c4b3a2918e7f6d5c
{
  "id": "cm8f2h6a10004qr9k5p2wm3xt",
  "event": "envelope.completed",
  "data": {
    "envelopeId": "cm7x2k9p40001qz8h3f7bn2ld",
    "sandbox": false,
    "subject": "Contrat de prestation Acme Corp",
    "status": "COMPLETED",
    "signatureLevel": "ADVANCED",
    "aesSignerCount": 2,
    "completedAt": "2026-05-27T08:42:13.000Z",
    "recipientCount": 2,
    "recipients": [
      { "email": "alice@acme.com", "name": "Alice Martin", "role": "SIGNER", "status": "SIGNED" },
      { "email": "bob@acme.com", "name": "Bob Durand", "role": "SIGNER", "status": "SIGNED" }
    ],
    "signedDocumentUrl": "https://storage.certyneo.com/signed/cm7x2k9p4.../contrat.pdf?X-Amz-Expires=604800&X-Amz-Signature=..."
  },
  "timestamp": "2026-05-27T08:42:13.521Z"
}

signedDocumentUrl on ennalta allekirjoitettu linkki, joka on voimassa 7 päivää tapahtumasta lähtien: se välttää toisen todentavan puhelun PDF:n hakemiseksi. Se puuttuu — eikä ole null — jos ennakkoallekirjoitus epäonnistui tai QES-kirjekuoressa, joka on täytetty ilman tallennettua asiakirjaa; palaa siihen GET /v1/envelopes/id/signed-document, joka on totuuden lähde. Huomio myös manuaalisesta uudelleenyrityksen takia dead-letter jonosta yli 7 päivää tapahtuman jälkeen: payload-linkki on vanhentunut, API-päätepiste ei.

HMAC-signaatti lasketaan raaka-ainetta vastaan älä muuta väliä, JSON-re-parsing muuttaa usein avainten järjestystä ja rikkoo tarkistuksen.

Tarkista HMAC-sääntö

Jokainen pyyntö allekirjoitetaan webhook-salaisuudellasi (näytetään vain kerran tilauksen luomisen yhteydessä). Allekirjoitus välitetään `X-Certyneo-Signature` -otsikossa: se on pyynnön raakarunko HMAC-SHA256, koodattu heksadesimaalilla, ilman etuliitettä tai aikaleimaa. Tarkista AINA allekirjoitus ennen payload-käsittelyä — ilman tätä vaihetta kuka tahansa voi väärentää tapahtuman ja kutsua päätepistettäsi.

Node.js / TypeScript

import crypto from "node:crypto";

export function verifyCertyneoSignature(
  rawBody: string,
  signatureHeader: string,
  secret: string,
): boolean {
  // X-Certyneo-Signature = HMAC-SHA256(secret, rawBody), hex-encoded.
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const received = Buffer.from(signatureHeader, "hex");
  const computed = Buffer.from(expected, "hex");

  // timingSafeEqual throws when the two buffers differ in length —
  // a malformed header must return false, not crash the handler.
  if (received.length !== computed.length) return false;

  // timingSafeEqual to mitigate timing attacks.
  return crypto.timingSafeEqual(computed, received);
}

Python

import hashlib
import hmac


def verify_certyneo_signature(
    raw_body: bytes, signature_header: str, secret: str
) -> bool:
    """Verify a Certyneo webhook signature.

    `X-Certyneo-Signature` holds the hex-encoded HMAC-SHA256 of the
    raw request body, computed with your webhook secret.
    """
    expected = hmac.new(
        secret.encode("utf-8"),
        raw_body,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(expected, signature_header)

Yleinen virhe

Älä käytä `===` tai `==` vertaamaan odotettua ja vastaanotettua allekirjoitusta. Käytä aikavarmaa toimintoa (`crypto.timingSafeEqual` Node, `hmac.compare_digest` Python).

Uudelleenkäsittelypolitiikka

Jos endpoint-palvelusi vastaa liian hitaasti, kieltää yhteyden tai palauttaa 5xx-virheen (tai 429-koodin), yritämme uudelleen eksponentiaalisella backoff-strategialla: yhteensä 5 yritystä noin 1 h 20 minuutissa. Viidennen yrityksen jälkeen tapahtuma siirtyy dead-letter jonoon — se on nähtävissä ja toistettavissa kojelaustasi, mutta sitä ei enää uudelleen yritetä automaattisesti. Eksplisiittinen kieltäytyminen (401, 403, 404, 410, 422 jne.) ei sitä vastoin ole koskaan uudelleen yritetty: vastaus ei muuttuisi, tapahtuma siirtyy suoraan dead-letter jonoon.

YritysKello ennen yritystäAikaa kulunut sitten tapahtumasta
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 15 min21 min
#5+ 1 h1 h 21

Ilmoitetut aikarajat ovat minimeja: retry-skannaus on cron-aikaistettu, joten yritys voi lähteä hieman teoreetista aikaa myöhemmin. Hylätyt tapahtumat on lueteltu kohdassa Webhooks → Virheet, joissa on manuaalisen toiston painike ja rajoittamaton säilytysaika. Endpoint, joka epäonnistuu viisi kertaa peräkkäin – tai vastaa 404/410 – poistetaan käytöstä automaattisesti ja saat siitä ilmoituksen sähköpostilla: aktivoi se uudelleen korjaamisen jälkeen, laskuri nollautuu (mikä tahansa onnistunut toimitus nollaa sen myös).

Testaus ilman oikeaa kirjekuorta

Luo ensin tilaus — vastaus sisältää `secret`-avain, joka on tarpeen HMAC-verifiointiin ja näytetään vain kerran. Asetuksista → Webhooks -painikkeesta « Testaa » lähettää POST-pyynnön, joka on allekirjoitettu täsmälleen kuin todellinen toimitus, ja näyttää palvelimesi raakavastauksen: se on nopein tapa vahvistaa käsittelijäsi, paikallisesti ngrok:n kautta tai jatkuvassa integraatiossa.

# Create a subscription — "secret" is returned once, store it now.
curl -X POST https://api.certyneo.com/v1/webhooks \
  -H "Authorization: Bearer $CERTYNEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your.app/webhooks/certyneo",
    "events": ["envelope.completed", "recipient.signed"]
  }'

URL:n on oltava julkisesti saavutettavissa HTTPS:n kautta: yksityinen osoite tai localhost hylätään SSRF-suojauksella tilauksen luomisen yhteydessä.

Tilaukset erotetaan ympäristöittäin kuten avaimet: sk_test_-avaimella luotu webhook vastaanottaa vain testikuorien tapahtumia, sk_live_-avaimella luotu webhook vain todellisten kirjekuorien tapahtumia. Testikuori ei siis koskaan saavuta tuotanto-URL-osoitettasi, ja jokainen hyötykuorma ilmoittaa "sandbox" (true tai false).

6 käytäntöä

  • Tarkista HMAC-signaatti ennen kehon lukemista käytä aikaa säästävää vertailua.
  • Poista duplikaatit `X-Certyneo-Delivery-Id` -otsikon perusteella (sama kuin `id` rungossa) tallentamalla jo nähdyt tunnisteet tietokantaan — uudelleenyritys voi toimittaa tapahtuman, jonka olet jo käsitellyt, jos 2xx hävisi, samalla tunnuksella joka yrityksellä.
  • Vastaa HTTP 2xx enintään 10 sekunnissa, sitten käsittele asynkronisesti (jono). Tämän jälkeen toimitus katkeaa ja lasketaan virheeksi.
  • HMAC-tarkastus epäonnistuu usein näkymättömän BOM:n tai valkoisen tilan vuoksi.
  • Valvo Webhooks → Virheet -sivua: sinut ilmoitetaan sähköpostilla, jos endpoint poistetaan käytöstä, mutta ei tapahtuma kerrallaan – tapahtuma, joka kuluttaa yritykset loppuun, sinun on mentävä toistamaan se itse.
  • Suodata tapahtumat tilauksen yhteydessä eikä käsittelijässäsi, ja pysy 5 tilauksen rajalla tiliä kohti.

Et ole velvollinen isännöimään päätepistettä näiden tapahtumien vastaanottamiseksi: liitin asettaa tilauksen puolestasi ja käynnistää työnkulun suoraan kirjekuoritapahtumaan. Katso Certyneo Power Automatea ja Microsoft 365:tä varten.

Lue lisää

Valmiina yhdistämään järjestelmänne?

Webhookit ja REST-API sisältyvät Standard-planista lähtien. Luo tilisi, generoi `sk_test_` -avain ja yhdistä päätepistesi hiekkalaatikossa ennen siirtymistä tuotantoon.