Handleidingen · REST API
De callback ontvangen en verifiëren
Een aanvraag met een callbackUrl eindigt met een ondertekende POST naar die URL: de antwoorden zodra de ontvanger indient, of het bericht dat hij is verlopen of geannuleerd. Deze handleiding draait een ontvanger op je eigen machine, controleert de handtekening, en print de antwoorden.
Last checked
Je hebt een API-token en een gepubliceerd formulier nodig, zoals in Een aanvraag versturen met de REST API, en Node of Python op je machine. Wat een callback draagt, en hoe nieuwe pogingen werken, staat in Callbacks & ondertekening.
1. Kopieer het ondertekeningsgeheim
formbase ondertekent elke callback met het ondertekeningsgeheim voor aanvragen van je werkruimte. Open OAuth en API-sleutels in de zijbalk van de werkruimte en zoek de kaart Ondertekeningsgeheim voor aanvragen:

Klik op de kopieerknop en bewaar het geheim in een omgevingsvariabele in de terminal waar je ontvanger zal draaien:
export FORMBASE_SIGNING_SECRET='rqs_...'Genereer het niet opnieuw
De derde knop maakt een nieuw geheim aan, en het oude stopt meteen met werken, ook voor callbacks die al onderweg zijn. Je hebt dit niet nodig om deze handleiding te volgen.
2. Schrijf de ontvanger
Bewaar de functie verify uit Callbacks & ondertekening naast je ontvanger: als
verify.mjs voor Node, omdat het import gebruikt, of als verify.py voor Python. De ontvanger leest
de ruwe body, controleert de handtekening, en parseert pas daarna de JSON:
import http from 'node:http'
import { verifyFormbaseCallback } from './verify.mjs'
const secret = process.env.FORMBASE_SIGNING_SECRET
http
.createServer((req, res) => {
const chunks = []
req.on('data', (chunk) => chunks.push(chunk))
req.on('end', () => {
const rawBody = Buffer.concat(chunks).toString('utf8')
const signature = req.headers['x-formbase-signature'] ?? ''
if (!verifyFormbaseCallback(rawBody, signature, secret)) {
console.log('rejected: bad signature')
res.writeHead(401).end()
return
}
const event = JSON.parse(rawBody)
console.log(event.type, event.data.request.externalId, JSON.stringify(event.data.answers))
res.writeHead(200).end()
})
})
.listen(8787)import json, os
from http.server import BaseHTTPRequestHandler, HTTPServer
from verify import verify_formbase_callback
SECRET = os.environ['FORMBASE_SIGNING_SECRET']
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
raw_body = self.rfile.read(int(self.headers['Content-Length']))
signature = self.headers.get('X-formbase-Signature', '')
if not verify_formbase_callback(raw_body, signature, SECRET):
print('rejected: bad signature', flush=True)
self.send_response(401)
self.end_headers()
return
event = json.loads(raw_body)
print(event['type'], event['data']['request'].get('externalId'), event['data'].get('answers'), flush=True)
self.send_response(200)
self.end_headers()
HTTPServer(('', 8787), Handler).serve_forever()Hash de body precies zoals hij is binnengekomen. Hem parsen en terug omzetten naar JSON verandert de bytes, en de handtekening komt dan niet meer overeen.
3. Draai hem en geef hem een publieke URL
Start de ontvanger:
node receiver.mjs # or: python3 receiver.pyformbase roept alleen publieke HTTPS-adressen aan, dus localhost volstaat niet. Terwijl je test, geeft een tunnel je machine
er een.
Cloudflare’s quick tunnel
heeft geen account nodig. Draai hem in een tweede terminal:
cloudflared tunnel --url http://localhost:8787Dat print een adres zoals https://horn-cod-classics-arab.trycloudflare.com. ngrok en vergelijkbare tools werken op dezelfde
manier. Gebruik in productie de eigen HTTPS-URL van je server in plaats daarvan.
4. Verstuur een aanvraag met een callback-URL
Maak een testaanvraag aan zoals in de vorige handleiding, en voeg
callbackUrl toe met het tunneladres. Gebruik je eigen formulier-id:
curl -X POST https://api.formbase.so/api/v1 \
-H "Authorization: Bearer $FORMBASE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"method": "requests.create",
"params": {
"formId": "jx75hdx8vb5hy1x85gm17nqgkn8f674g",
"recipient": { "email": "ada@acme.example", "name": "Ada Lovelace" },
"prefill": { "company_name": "Analytical Engines Ltd" },
"readonly": ["company_name"],
"externalId": "supplier-2044",
"idempotencyKey": "supplier-2044",
"callbackUrl": "https://horn-cod-classics-arab.trycloudflare.com/formbase",
"test": true
}
}'Gebruik een nieuwe idempotencyKey: dezelfde sleutel met een andere body wordt geweigerd. Open de url uit het
antwoord, beantwoord het formulier, en dien in.
5. Zie de callback binnenkomen
Een paar seconden nadat je indient, print de ontvanger het event-type, jouw externe id en de antwoorden. De Node-ontvanger print:
request.completed supplier-2044 {"certificate_of_incorporation":[{"name":"certificate-of-incorporation.pdf","size":635,"type":"application/pdf","url":"https://api.formbase.so/api/storage/...",...}],"company_name":"Analytical Engines Ltd","contact_email":"ada@acme.example","do_you_accept_our_30_day_payment_terms":"yes","vat_number":"GB123456789"}Om een weigering te zien, stuur je de ontvanger zelf een willekeurige POST, bijvoorbeeld
curl -X POST -d ‘’ http://localhost:8787. Die heeft geen geldige handtekening, dus de ontvanger print
rejected: bad signature en antwoordt met 401.
Voordat je live gaat
Antwoord binnen 10 seconden met 2xx. formbase markeert de callback als afgeleverd bij elke 2xx. Een 5xx, een 408, een 429 of een timeout wordt zo’n vier uur lang opnieuw geprobeerd; elke andere 4xx, zoals de 401 hierboven, stopt de nieuwe pogingen. Zie Nieuwe pogingen.
Vertak op
type.Dezelfde URL hoort ook
request.expiredenrequest.canceled, die geen antwoorden dragen.Verwerk elk event maar één keer. Een nieuwe poging draagt hetzelfde
id. Bewaar de id’s die je al hebt verwerkt en sla een herhaling over.Controleer
test.Een callback van een testaanvraag heeft
“test”: true. Laattestweg bijrequests.createvoor echte ontvangers.
Als je ontvanger langer offline was dan de nieuwe pogingen duren, zijn de antwoorden niet verloren. Lees ze met requests.get,
of speel de callback opnieuw af met requests.replayCallback of vanaf de
pagina Aanvragen.