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.

  1. Make a program (dashboard or POST /programs) with the budget, the amount and per_recipient={"lifetime": 1} if each person is paid once.
  2. 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.
  3. Add a question for the respondent's mobile number to the form (and their meter, if you pay ZESA).

In Typeform

  1. Add a phone number question (or pass the number in as a hidden field if you already know it).
  2. 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.
  3. Each post carries form_response.token, unique per response. That is the completion_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.