> Documentation index: [/llms.txt](/llms.txt). Every page as one file: [/llms-full.txt](/llms-full.txt).

# Send events to an app

Send events to an app [#send-events-to-an-app]

An outside system posts events to a route the app serves and signs each one, following [Standard Webhooks](https://www.standardwebhooks.com/). The app checks the signature; the platform does not.

Steps [#steps]

1. In the app's chat, ask App Builder for a receiver:

   ```text
   Add POST /api/hooks/findings following Standard Webhooks: signature "v1," +
   base64(HMAC-SHA256(key, f"{id}.{timestamp}.{raw_body}")), where key is the
   base64-decoded app secret RADAR_WEBHOOK_SECRET. Read id, timestamp and signature from
   the query parameters webhook_id, webhook_timestamp and webhook_signature.
   Return 401 for a bad signature or a timestamp more than 5 minutes off. Store
   webhook_id; a repeat returns 200 with the existing row and creates nothing.
   ```

2. Set `RADAR_WEBHOOK_SECRET` as an app secret: 24 to 64 random bytes, base64-encoded.

3. Set the app's visibility to **Public**.

4. Sign and send each event from the outside system:

   ```python
   import base64, hashlib, hmac, json, os, time, uuid
   import httpx

   key = base64.b64decode(os.environ["RADAR_WEBHOOK_SECRET"])
   body = json.dumps({"title": "Nightly backup failed", "severity": "high"})
   webhook_id, timestamp = f"msg_{uuid.uuid4().hex}", str(int(time.time()))
   signature = base64.b64encode(
       hmac.new(key, f"{webhook_id}.{timestamp}.{body}".encode(), hashlib.sha256).digest()
   ).decode()
   httpx.post(
       "https://<app_id>.<apps domain>/api/hooks/findings",
       content=body,
       params={"webhook_id": webhook_id, "webhook_timestamp": timestamp, "webhook_signature": f"v1,{signature}"},
       headers={"content-type": "application/json"},
   )
   ```

   **Open in new tab** on the app's card shows the app's own address.

Why the signature is in the query string [#why-the-signature-is-in-the-query-string]

Standard Webhooks sends `webhook-id`, `webhook-timestamp` and `webhook-signature` as headers. The app gateway does not pass those headers to the app, so the sender puts them in the query string. The secret itself never travels.

Retries [#retries]

Resend a failed event with the same `webhook_id`; the app answers with the row it already stored and creates nothing new.
