formbasedocs
Gå til appenAppen

Forespørsler

Callbacks og signering

Når en forespørsel når slutten — fullført, utløpt eller kansellert — sender formbase en signert POST-varsling til URL-en automatiseringen din oppga. Det kallet er det som gjenopptar kjøringen.


Hva som utløses, og når

HendelseNår
request.completedMottakeren sendte inn. Bærer svarene.
request.expiredUtløpstiden passerte mens forespørselen fortsatt ventet.
request.canceledDu eller automatiseringen din trakk den tilbake.

Alle tre ankommer samme URL, så grener på type før du antar at det finnes svar. Det er hele poenget med å utløse ved enhver avslutning: en arbeidsflyt parkert på en kunde gjenopptas enten de svarte, ignorerte deg, eller ble kalt av.

Hva som ankommer

POST body
json
{
  "id": "evt_kj7...",
  "type": "request.completed",
  "createdAt": "2026-03-04T09:31:40.000Z",
  "apiVersion": "2026-09-24",
  "test": false,
  "data": {
    "request": {
      "id": "kd7...",
      "externalId": "run-42",
      "status": "completed",
      "outcome": "approve",
      "language": "en",
      "recipient": { "email": "ada@acme.com", "name": "Ada" },
      "metadata": { "runId": "run-42" },
      "context": { "case_id": "CASE-9" },
      "createdAt": "2026-03-04T09:20:00.000Z",
      "completedAt": "2026-03-04T09:31:40.000Z"
    },
    "form": { "id": "j57...", "name": "Vendor onboarding", "snapshotId": "kx2..." },
    "submission": {
      "id": "jd7...",
      "respondentEmail": "ada@acme.com",
      "submittedAt": "2026-03-04T09:31:40.000Z",
      "updatedAt": null,
      "editCount": 0,
      "pdfUrl": null,
      "language": "en"
    },
    "answers": { "company_name": "Acme", "contacts": [{ "name": "Ada" }] },
    "display": { "company_name": "Acme", "contacts": "Ada" }
  }
}
  • data.request er alltid der — inkludert externalId, metadata, og context, uendret. Den bærer tidsstempelet for uansett hvilken avslutning som skjedde (completedAt, expiredAt, eller canceledAt med en valgfri cancelReason).

  • outcome er kun til stede når mottakeren svarte på et beslutningsspørsmål — approve, decline, eller changes.

  • form, submission, answers og display vises kun ved fullføring, i akkurat den formen en innsendingswebhook bærer. form.snapshotId er den nøyaktige publiserte versjonen mottakeren svarte på; submission.pdfUrl er en URL bare når skjemaet beholder en innsendings-PDF, og ellers null.

  • test er true når forespørselen ble opprettet i testmodus — forgren deg på det, eller forkast hendelsen.

  • answers er nøkkelsatt etter feltnøkkel, med gjentakende grupper nøstet som ett objekt per instans. Et valgsvar er valgets key fra fields.list, ikke etiketten dets; etiketten er i display, under samme nøkkel.

  • POST-en ankommer som Content-Type: application/json med User-Agent: formbase, og bærer X-formbase-Event-Id, X-formbase-Event-Type og X-formbase-Signature — slik at du kan avduplisere og rute før parsing.

Verifiser signaturen

Hver callback bærer en signaturheader, X-formbase-Signature: t={unix seconds},sha256={hex}. Hex-en er en HMAC-SHA256 av tidsstempelet, et punktum, og selve forespørselens rå body, beregnet med arbeidsområdens signeringshemmelighet for forespørsler.

To regler, uansett hvilket språk du bruker:

  1. Hash den rå bodyen, før noen parsing eller re-serialisering. Re-enkodet JSON er ikke de samme bytene.

  2. Sammenlign i konstant tid — crypto.timingSafeEqual, hmac.compare_digest — aldri med ==.

Signeringshemmeligheten for forespørsler

Én hemmelighet per arbeidsområde signerer hver callback fra den. Finn den under OAuth og API-nøkler i arbeidsområde-sidefeltet, i kortet Signeringshemmelighet for forespørsler. Den er maskert som standard; øye-knappen avslører den og kopier-knappen kopierer den. Det er ikke en engangsverdi — du kan komme tilbake og lese den igjen.

Nye forsøk

En callback får åtte forsøk: det første, deretter sju nye forsøk med minst 1, 2, 4, 8, 16, 32 og 60 minutters mellomrom. formbase ser etter forfalte nye forsøk hvert 30. minutt, så et nytt forsøk kan komme opptil en halvtime etter at mellomrommet er ute, og det siste forsøket kommer om lag fire timer etter at forespørselen avsluttet. Hvert forsøk bærer de samme bytene og samme id: nyttelasten er frosset i øyeblikket forespørselen avsluttet, så et nytt forsøk beskriver hva som skjedde da, ikke hvordan forespørselen ser ut nå. Mål-URL-en og signeringshemmeligheten leses ved hvert forsøk, og fryses ikke sammen med den.

Din responsHva formbase gjør
2xxFerdig. Callbacken merkes som levert.
408, 429, 5xxNytt forsøk, med respekt for Retry-After når du sender en.
Annen 4xxStopper. Endepunktet ditt avviste kallet; å prøve samme body på nytt kan ikke hjelpe.
Tidsavbrudd eller tilkoblingsfeilNytt forsøk etter samme plan.
Blokkert URLStopper umiddelbart. En vert som ikke lar seg slå opp, en privat adresse, eller en URL uten HTTPS kan aldri bli tillatt. Omdirigeringer følges aldri, så en 3xx stopper også.

Hvis budsjettet går tomt — mottakeren din var nede en ettermiddag — er callbacken ikke tapt. Forespørselen får merket Callback feilet, arbeidsområdeeieren får én e-post med verten, årsaken og antall forsøk, og svarene forblir lesbare gjennom requests.get. For å sende den på nytt, åpne forespørselen på Forespørsler-siden og trykk Kjør på nytt, eller kall requests.replayCallback. Den sender den samme frosne nyttelasten på nytt med samme id, som er akkurat hva en mottaker som avdupliserer ønsker.

Abonnementer hører de samme hendelsene

En callback-URL hører til én forespørsel. Når hver forespørsel på et skjema skal nå den samme mottakeren, abonner én gang i stedet: formbase-appene for Zapier og n8n gjør dette for deg, og webhooks.create gjør det fra kode, med hendelsestypen request_completed, request_expired eller request_canceled. Et abonnement mottar denne samme konvolutten, signert med sin egen hemmelighet i stedet for arbeidsområdens signeringshemmelighet for forespørsler, med sin egen hendelses-ID og sitt eget forsøksbudsjett. En forespørsel som har både en callback-URL og et matchende abonnement, utløses to ganger, én gang til hver. Kjør på nytt sender bare callbacken på nytt; et abonnement prøver på nytt på egen hånd og pauser etter fem mislykkede forsøk.

En gjenopptakelses-URL er ikke autentisering

Arbeidsflytverktøy gir deg en vanskelig-å-gjette gjenopptakelses-URL, og det er fristende å behandle det som bevis. Det er en bærerhemmelighet — den kan lekke inn i logger, og den forteller deg ikke at bodyen ikke er tuklet med. Verifiser signaturen i den gjenopptatte grenen også.