Build
Typeform guide
A signed Typeform webhook, checked, then rewarded.
How it fits together
Typeform can post each submission to a URL, but it cannot sign a request the way Jusa's completions endpoint
wants or hold a secret key safely. So a small relay of yours sits between them: Typeform posts to it, it checks the
post really came from your form, and it calls POST /rewards, keyed on the submission so each is paid
once, forever.
- Make a program (dashboard or
POST /programs) with the budget, the amount andper_recipient={"lifetime": 1}if each person is paid once. - Make a restricted key with only
rewards:write, and give it to the relay. If it ever leaks, it can reward from that program's budget and do nothing else. - Add a question for the respondent's mobile number to the form (and their meter, if you pay ZESA).
In Typeform
- Add a phone number question (or pass the number in as a hidden field if you already know it).
- Open Connect → Webhooks, add your relay's URL and set a secret. Typeform then
signs each post with
Typeform-Signature: sha256=<base64 HMAC-SHA256 of the body>, which the relay checks. - Each post carries
form_response.token, unique per response. That is thecompletion_id.
The relay
import base64, hashlib, hmac, os, jusa from flask import Flask, request, abort jusa.api_key = os.environ["JUSA_REWARDS_KEY"] app = Flask(__name__) @app.post("/typeform") def typeform(): digest = hmac.new(os.environ["TYPEFORM_SECRET"].encode(), request.get_data(), hashlib.sha256).digest() expected = "sha256=" + base64.b64encode(digest).decode() if not hmac.compare_digest(expected, request.headers.get("Typeform-Signature", "")): abort(403) r = request.get_json()["form_response"] phone = next(a["phone_number"] for a in r["answers"] if a["type"] == "phone_number") jusa.Reward.create( program=os.environ["JUSA_PROGRAM"], recipient={"phone": phone}, completion_id=r["token"], idempotency_key=f"typeform-{r['token']}", ) return "", 200
Typeform sends numbers in international form (+2637…); Jusa takes any Zimbabwean format. Test with a
test key first, then swap it.