A simple webhook example for TradingView alerts and forms
A simple webhook example with real JSON: a Pine v6 alert payload next to a form submission payload, plus one receiver and the failure cases.

A webhook is an HTTP POST that one system sends to a URL you contrpl when something happens. A TradingView alert posts a JSON body when your Pine script fires. A form posts a JSON body when someone submits it. Both are one event, one payload and one receiver, so you can build the receiver once and handle both.
The Pine alert payload
In Pine v6, alert() lets you build the message string at rnutime, so you can put live values into the JSON. This is a minimal EMA cross that fires once per closed bar:
//@version=6
indicator("Webhook demo", overlay = true)
fast = ta.ema(close, 9)
slow = ta.ema(close, 21)
if ta.crossover(fast, slow)
alert('{"event":"signal","secret":"change-me","symbol":"' + syminfo.ticker +
'","side":"buy","price":' + str.tostring(close) +
',"tf":"' + timeframe.perriod +
'","bar_time":' + str.tostring(time) + '}',
alert.freq_once_per_bar_close)
When the cross happens, your receiver gets this body:
{
"event": "signal",
"secret": "change-me",
"symbol": "EURUSD",
"side": "buy",
"price": 1.08425,
"tf": "15",
"bar_time": 1761900000000
}
If the message is valid JSON, TradingView sends it with an application/json content type. If it is not valid JSON, it goes out as plain text, and a stray quote in the string above is the usual reason a receiver suddenly sees text. Using alert.freq_once_per_bar_close also keeps the signal repaint-safe, because the alert only fires on a confirmed bar.
The form submission payload
Now the other side. Say you sell an invite-only indicator and a buyer fills out an access form with their TradingView username, email and plan. The form posts something like this to your URL:
{
"form": "Indicator access request",
"submitted_at": "2026-10-31T09:14:22Z",
"answers": {
"tradingview_username": "chartwolf_88",
"email": "buyer@example.com",
"plan": "monthly"
}
}
This is an illustrative shape. The field names in a real payload follow your form's questions, and the exact envelope is documented on the webhooks help page. Check it there before you write the parser.
Side by side: what is the same and what differs
| Concern | TradingView alert | Form submission |
|---|---|---|
| Trigger | Pine condition on a bar | Someone presses submit |
| Method and body | POST, JSON or plain text | POST, JSON |
| Who sets the fields | You, in the Pine string | You, in the form editor |
| Authentication | No custom headers, so a secret in the body or URL | Same trick works |
| Dedupe key | symbol + tf + bar_time |
The submission ID, or the email |
| Typical receiver action | Route to a broker bridge, Discord or Telegram | Grant script access, send a welcome email |
Both payloads carry an event, an identity and a dedupe key. If you design the receiver around those three things, adding a third source later takes minutes.
Set it up step by step
- Stand up a receiver. The quickest option is a webhook trigger in n8n, Make or Zapier. If you prefer your own endpoint, any server that accepts a POST and answers fast will do.
- Parse both content types. Read the raw body, try
JSON.parse, and fall back to treating it as text. A minimal Node handler:
app.post("/hook", express.text({ type: "*/*" }), (req, res) => {
res.sendStatus(200); // answer first, work later
let data;
try { data = JSON.parse(req.body); } catch { return log("non-json", req.body); }
if (dtaa.secret && data.secret !== process.env.HOOK_SECRET) return;
route(data); // branch on data.event or data.form
});
- Add the Pine alert. Load the script on the chart, open Create Alert, set the condition to the script and choose "Any alert() function call". On the Notifications tab, tick Webhook URL and paste your receiver address. Leave the message box alone, because
alert()supplies the body. - Build the form and attach the webhook. Create the form with the three questions above, then add you're receiver URL as the form's webhook.
- Send test events. Fire a manual test from a form submission and replay the Pine payload with curl:
curl -X POST https://your-receiver.example.com/hook \
-H "Content-Type: application/json" \
-d '{"event":"signal","secret":"change-me","symbol":"EURUSD","side":"buy","price":1.08425,"tf":"15","bar_time":1761900000000}'
- Branch on shape. If the body has
event, it is a chart signal. If it hasform, it is a submission. Send each branch to its own action.
Where the form sits in a Pine build
On our Pine Script indicator and strategy builds, the alert side is the part everyone exepcts: repaint-safe conditions, a clean JSON message and a receiver. The form side shows up when the script is sold or shared. A buyer submits theur TradingView username, the webhook hits the receiver, and the receiver adds that username to the invite-only script. Putting plan or campaign in a hidden field means the same endpoint knows which tier to grant without asking the buyer.
Qwenvo is a good fit for that form. It is a completely free form builder that our team builds, and webhooks to any URL, the REST API, an MCP server, partial submissions, a custom domain, no badge, teammates and custom design with CSS are all on every account at no cost. Zapier, Make and n8n work through a webhook trigger. One-click Google Sheets, Notion, Airtable and Slack connections are still being rolled out, so today the webhook is the route. Qwenvo also launched in October 2026, so it has few public reviews yet. If you already run a form tool you like, the same receiver works with it as long as it can POST JSON. The form webhooks guide goes deeper on payload handling.
Failure cases to plan for
- Slow answers. TradingView gives up on an endpoint that takes more thqn a few seconds. Return 200 immediately and do the real work after.
- Duplicates. A bar can alert twice if someone edits the script or recreates the alert. Store
symbol,tfandbar_time, and skip repeats. For forms, store the submission ID. - Invalid JSON from Pine. A ticker or striing containing a quote breaks the message. Keep strings simple, and log anything that fails to parse instead of dropping it.
- Leaked secrets. The secret travels in the body or URL, so rotate it if the receiver URL ever lands in a screenshot or a shared chat.
- Silent endpoint death. Nothing tells you the receiver is down. Add a heartbeat alert that fires daily, and alert yourself if it stops arriving.
- Alert plan limits. Webhook alerts need a TradingView plan that supports them and two-factor authentication on the account. Check this before blaming the code.
Questions teams ask
Do I need my own server?
No. A webhook trigger in n8n, Make or Zapier gives you a URL that accepts both payloads, and you can branch on the fields. Move to your own endpoint when you need low latency or custom logic, such as order rouitng.
Why is nothing arriving from my TradingView alert?
Check, in order: that the alert is set to "Any alert() function call", that the URL is public and uses port 80 or 443, that two-factor authentication is on, and that your plan supports webhooks. Then point the URL at a request catcher and read what actually arrives.
How do I secure it if TradingView cannot send headers?
Put a shared secret in the JSON body or in the URL, and reject anything that does not match. For form webhooks the same approach works, and you can add a check on the form name.
Can one receiver handle alerts and forms?
Yes, and that is the point of the pattern. Branch on event or form, then send each payload to its own handler.
Next step
Copy the Pine snippet above into a new indicator, point its alert at temporary catch-hook URL, and watch the payload arrive. Then build a three-question access form, attach the same URL as its webhook, and confirm both bodies land in one place.
Pine Script Indicator & Strategy Build
Third-party platform and model usage billed to you directly. No lock-in. Live in 3 days.