01 / The existing evidence
Signed in every delivery
HMAC-SHA256, timestamp and event ID in every delivery. On a retry the event ID stays the same.
- Adapter in source code
- Present
- Receipt in the target account
- Not checked
Peak Forms · Developers
Peak One sends every submission as signed JSON to your server. You verify the signature, detect duplicates by ID and process the rest as usual. You create forms themselves as drafts through the agent API; they only go live after your approval.
Start for freeHMAC-SHA256, timestamp and event ID in every delivery.
Peak FormsYour answeremailA readable field path from the adapter. No transfer, no verified delivery.
| Peak Forms | HubSpot property |
|---|---|
email | |
| First name | firstname |
| Last name | lastname |
| Lifecycle stage | lifecyclestage |
| Lead status | hs_lead_status |
A mapping shows the translation. Only a test in the authorised account shows that data arrived.
02 / How to start
The body always has the same envelope. Under data you find lead, answers, source and campaign.
Enter a public https address. Internal addresses and IP ranges are refused.
Peak One creates a secret with the prefix whsec_. Keep it server-side.
“Send test” delivers a sample event. Check signature and duplicate detection.
Trust needs clarity
01 / The existing evidence
HMAC-SHA256, timestamp and event ID in every delivery. On a retry the event ID stays the same.
02 / Your check
“Send test” delivers a sample event. Check signature and duplicate detection before real responses flow.
03 / The clear limit
The agent API creates and changes drafts and requests publishing, which a person approves. There is no API to fetch all responses, no SDK and no published command-line tool yet. You get responses by webhook and CSV export, a single response only with its own read permission.
The next step
Create a free account and send yourself the first test event.
Start for freeCheck in the target account what actually arrived.
Sample code
The signed text is “<timestamp>.<raw body>” with your webhook secret. Verify on the raw body before you parse it.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyPeakWebhook(secret, rawBody, header, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const t = Number(parts.t);
if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const want = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
const got = Buffer.from(parts.v1, "hex");
return want.length === got.length && timingSafeEqual(want, got);
}<iframe src="https://app.peak-atlas.com/f/<public-id>?embed=1"
title="Kontakt" loading="lazy"
style="width:100%;min-height:640px;border:0"
data-peak-form="<public-id>"></iframe>curl -X POST https://app.peak-atlas.com/api/v1/actions/form.create_draft \
-H "Authorization: Bearer $PEAK_AGENT_TOKEN" \
-H "content-type: application/json" \
-H "Idempotency-Key: form-erstgespraech-01" \
-d '{"template_id":"lead"}'
# 200 {"status":"ok","data":{...},"object":{"type":"form","id":"...","url":"..."}}curl -X POST https://app.peak-atlas.com/api/v1/actions/form.request_publish \
-H "Authorization: Bearer $PEAK_AGENT_TOKEN" \
-H "content-type: application/json" \
-H "Idempotency-Key: form-erstgespraech-publish-01" \
-d '{"form_id":"<id>","expected_revision":3,"expected_fingerprint":"<fingerprint>"}'
# 202 {"status":"approval_required","approvalId":"..."}
# Status: GET /api/v1/approvals/<approvalId>Up to six attempts, spread from one minute to twelve hours, always with the same ID. Respond quickly with 2xx and process asynchronously.
Yes, with the full embed code from Peak Forms. It contains a short script that follows the height.
Yes, under data.answers, plus name, e-mail and campaign details where available.
Not as a list. Use the webhook for new responses and the CSV export for existing ones. An agent with the forms.responses.read permission reads a single response through form.get_decision_card.
Create an agent under Settings → AI & agents. Its key starts with peak_agt_, works for one workspace and cannot publish without approval.