Docs
A key on the apply. A link for people who already applied. Same object. Different join.
Install the button
Paste this. Replace YOUR_KEY with the key from Get the button. iOS Safari is supported.
<script src="https://cdn.sayhiapply.com/apply.js"></script><button data-say-hi="YOUR_KEY"></button>
Same object as on the form. The CDN URL in the snippet is the contract.
The button never asks for email. Their form already has who. On Send we write a hidden
say_hi_id. Read application_id if the parent form or
data-application-id gives one.
The hidden say_hi_id is a claim. Verify it server-side with
GET /v1/hi/{id} and your secret key before you trust it. That
response is status only — never a video URL.
Your page must be https://. Camera and mic cannot open on
http://. The script is already https. The page you paste it on must be too.
If the page is not https, or the device has no camera, they can say hi on another phone. The button shows that link. After they send, they copy a code (not a video) and paste it back into the form. Only you watch it, in inbox, when you are signed in.
Content Security Policy
If your page already has a CSP, add these. The button is not an iframe. Pill and sheet CSS
load as files from the CDN — no unsafe-inline.
script-src https://cdn.sayhiapply.com
style-src https://cdn.sayhiapply.com
connect-src https://cdn.sayhiapply.com https://api.sayhiapply.com https://hi.sayhiapply.com
media-src blob: mediastream:
frame-src 'none'
Permissions-Policy: camera=(self), microphone=(self), geolocation=(self). Location is optional. Camera and mic are not.
Drops into
Drops into any page you can paste a script.
WordPress · Webflow · Framer · Squarespace · Wix · Shopify · HubSpot · HTML
Also: Bubble, Unbounce, Leadpages, Instapage, Drupal, Ghost, Carrd, Duda. On WordPress, Gravity Forms and WPForms count. The script lives on the page, next to the form.
Or send a link
Screen them with a link from your ATS, email, SMS, a message, or a QR you print.
Use a link when you cannot paste a script. Greenhouse, Lever, Workable, Ashby, Typeform, Tally, Jotform, and Google Forms host the form. You cannot drop the button inside those. Send the link, or put Say Hi on a page you own next to them.
The link has no form of theirs to carry the id. After they record: Next, then the email they used on the apply, then Send. That email is the join. We do not push into Lever, Greenhouse, or any ATS. You line it up against the apply you already have. Optional ref on the URL; ref wins if both exist. We email them the id only — a receipt, not the tape, not an account, not marketing.
Ask for the code on your form and the match is exact. Add one short-answer question next
to the link, labelled something like “Paste your Say Hi code”. After Send we show them that
code and email it to them, so they can paste it there and then or come back to it later.
It is the same id you see in your inbox and on the webhook —
never a video, never a file — so matching on it leaves nothing to line up by eye.
Optional, and it does not replace the email. The apply email is still required before Send, so a candidate who ignores the question still reaches you. The code just saves you the matching, and beats the email whenever both arrive.
We do not ship a QR. You paste the link into whatever you already print.
The button
Introduce yourself
Always says 👋 Say Hi. All black type, no blue. Same on the form and on the link.
Beside it: Introduce yourself. Default helper. Not inside the pill. Your prompt replaces it. Do not show both.
The only other label: 👋 Say Hi on [their name], if they must. Clients do not restyle it to Upload video.
Create a link
No code. Create a link. Send the URL to people who already applied. Email on the link is the join. The button never asks for it.
What “live on device” means
Camera or mic on that phone. No file picker. No YouTube attach. They record there.
What you get back
When they finish: a video or a voice note. When they recorded. Where they were. On a link: the email they used on the apply. Inbox, or a webhook you add later.
Webhook
Webhook ships with the product. Field exists on Get the button.
email is set on the link door only. Empty on the button.
ref is optional on the link URL;
ref wins if both ref and email exist.
POST your HTTPS URL. We sign timestamp.body as HMAC-SHA256. Header X-Say-Hi-Signature: v1=… plus X-Say-Hi-Timestamp. Reject a timestamp older than five minutes. Compare the hex in constant time.
{
"id": "hi_...",
"mode": "video",
"name": "Alex",
"email": "alex@candidate.example",
"recorded_at": "ISO-8601",
"place": { "city": "Leeds", "lat": 53.8, "lng": -1.55 },
"application_id": "",
"answers": [
{ "prompt": "Why you?", "url": "https://…" }
]
}
email is present only on the link door, where they typed it. The
button never asks, so the field is absent there. It is self-declared and unverified — line
it up by eye, do not match on it alone.
place is null if they gave neither a
city nor GPS, and coordinates are rounded to two decimals.
url is signed and short-lived, and is left off when playback is
not configured. application_id and
ref arrive once connect persist ships; today they are empty.
Keep
We host 30 days. Download any time. Then we delete our copy. You keep what you saved. Not another ATS.
Meter
Install free. First 15 mins are free, then a quarter each.
After the 15, billed as $10 top-ups (not a monthly minimum). $10 = 40 mins at a quarter each.
Top-ups last 12 months from purchase. Oldest first.
Top-ups do not get refunded. Closing the account: leftover is forfeited.
The 15 free mins have no 12-month clock. They are once, until used.
A take bills at least 1 min. Longer recordings count as more than one min. Volume is a conversation.
We do not
Score the face. Pick your top three. Replace your ATS. Push into Lever, Greenhouse, or any ATS. Harvest a member list. Require a login from the person who says hi.