Lead attribution · your server
A lead file from your backend: one file, one line, every lead
A lead file from your backend is the quickest way for your own server to tell Skald about each lead: download one file for PHP, Node or Python, and call one function after the form is saved.
A lead file from your backend is a small file Skald gives you for your site's language. It signs each lead with your hook's secret, posts it to Skald, waits at most 3 seconds, and never throws, so a form never breaks because of Skald.
30-day free trial · no card · we set it up within one working day · free to self-host
// your own code saved the entry: $entry_id require_once __DIR__ . '/skald-lead.php'; skald_lead($entry_id, 'contact', $email, $name);
// your own code saved the entry: entryId const { skaldLead } = require("./skald-lead.cjs"); await skaldLead(entryId, "contact", { email, name });
# your own code saved the entry: entry_id from skald_lead import skald_lead skald_lead(entry_id, "contact", email=email, name=name)
Sample code. One file next to your code, one call after the entry is saved. The PHP file also reads the visitor ID from the form's skald_vid field or cookie by itself.
[01] it never breaks a form
What happens to your form when Skald is slow or down? The file gives up within 3 seconds.
The lead file sends one signed POST and waits at most 3 seconds. It never throws: it returns true when Skald kept the lead, and false for anything else, so your form carries on. Pick what goes wrong and watch each file's clock. Every lead lands in your lead attribution.
- PHP with curltrue · 0.04 s
- Node 18 or latertrue · 0.04 s
- Python 3.8 or latertrue · 0.04 s
Skald answers 200 and kept the lead. Each file returns true.
Each file gives up at 3 seconds and returns false: curl's CURLOPT_TIMEOUT 3, Node's AbortSignal.timeout(3000), Python's timeout=3. Skald may still keep the lead. Send it again later: a repeat counts once.
Nothing answers the connection. PHP stops first, at curl's 2-second connect limit. Node and Python stop at 3 seconds. All return false.
The port is closed, so the connection is refused at once. All return false, without waiting.
Skald answers 401 {"error": "bad signature"} at once, and Last deliveries counts it. All return false. A clock more than 5 minutes off does the same.
No secret in SKALD_LEAD_SECRET and none in the file, or an empty reference or action: the function returns false before sending anything.
Sample timings: real ones depend on your network. The 3 seconds still come out of your request's time: call the function after you answer the visitor, or from a queue, if that matters. The files do not send again by themselves.
[02] no lead lost
Check that Skald has every lead your server sent, and send the missing again
A lead lost on the way is never lost for good. Your server asks Skald which leads it holds, signed with the same secret, compares them with its own list, and sends the missing ones again with their own time. A resend of a lead already kept changes nothing.
your server's leads since Monday · sample
L-1001demoMon 09:12L-1002contactMon 11:40L-1003signupTue 08:05L-1004contactTue 16:22L-1005demoWed 10:01L-1006signupWed 13:30L-1007contactThu 09:47L-1008demoThu 15:15
the request
GET /traffic-hooks/example.com/leads?since=2026-10-19T00%3A00%3A00Z&limit=500 X-Skald-Signature: t=1792843200,v1=<hex HMAC-SHA256 of the signed line> signed: 1792843200.GET /traffic-hooks/example.com/leads?since=2026-10-19T00%3A00%3A00Z&limit=500
Press Ask Skald. Skald answers with each lead's reference, action, stage, time, label, test mark and when it arrived. Never a name, an email, an address or a visitor ID.
Sample leads; t is the time of the request, signed over the path and the query exactly as sent. Up to 500 rows a page, with a cursor for the next. It lists only the leads from this hook, and Skald keeps them 400 days by default.
[03] same server, behind Cloudflare
Your backend runs on the same server as Skald? Post to the local address.
When the public address goes through Cloudflare, a server that posts to its own public address can be refused with a 403, error 1010 or a bot check: Cloudflare takes the post for a bot. Post to Skald's local address instead, or allow the path in Cloudflare. The signature is the same either way.
the file posts tohttps://stats.example.com/traffic-hooks/example.com/lead
can be refused 403, error 1010 or a bot check: the file returns false.
[04] backfill
Backfill old leads on the right day: send each with its time and its offset
A lead sent late lands on the day in its at, not the day it arrived. Type a time, or pick one, and see the day it lands on in the site's own zone.
the site's time zone
arrived Sat 24 Oct, 11:00 in the site's zone · sample
- Thu 22 Oct
- Fri 23 Oct
- Sat 24 Oct
kept on Thu 22 Oct, 23:30 in the site's zone (03:30 UTC on 23 Oct)
Sample arrival, Saturday 24 October 2026 at 15:00 UTC. Times are read the way Skald reads them: ISO text, or Unix seconds (milliseconds when the number is that big). Send backfills slowly: 60 a minute for the hook, or up to 100 at once through the json hook.
[05] set up and limits
Set up the lead file in four steps
Make the secret, keep it on the server, add the file, then test it.
- Make secret. Skald: Settings > Integrations > Your site's server (lead) > Make secret.
- Keep the secret on the server. Set
SKALD_LEAD_SECRETin its environment, the settings your server hands to your code when it starts, kept out of your files. Every file reads it first. - Download the file.
skald-lead.php,skald-lead.cjsorskald_lead.py. Call its one function after the entry is saved. - Test it. Press Send a test event: read, never kept. Then send a real lead and look at Last deliveries. Your team's own tests go with
"test": true: listed, never counted.
What it does not do
- It sends once. When it returns false, sending again is your code's job, and [02] finds what is missing.
- It waits up to 3 seconds inside your request when Skald is slow.
- Journeys need the visitor ID. The page must pass it (the PHP file reads
skald_vidby itself; Node and Python take it asvid), and visitor IDs must be on for the site. They are off by default. - The same lead sent by this file and by Zapier counts twice. Send each lead one way.
- A file downloaded with the secret in it must stay out of public places and public code repositories.
- A time with no offset is a guess: Skald reads it in the site's zone.
keep reading
More on lead file from your backend
questions
Lead file from your backend FAQ
Straight answers, including what Skald does not do.
Which languages are there lead files for?
PHP (skald-lead.php), Node 18 or later (skald-lead.cjs, which also loads from an ES module) and Python 3.8 or later (skald_lead.py). WordPress has its own plugin. Any other language can sign and post the JSON itself: the help page has the exact body and signature, with curl, Node, Python and PHP examples.
Can the lead file break my form?
No. Each function waits at most 3 seconds, never throws, and returns true when Skald kept the lead. When it returns false, your code can send the lead again later. A repeat is never counted twice.
Where does the secret go?
Best in an environment variable on your server named SKALD_LEAD_SECRET: every file reads it first. A file downloaded right after you made the secret holds it and works as it is, but keep that file out of public places and public code repositories. Never put the secret in a web page.
How do I join a lead to the visitor's journey?
With Skald's script tag on the page and visitor IDs on, the page knows the visitor ID as skald('id'). Bring it to your server in a hidden form field named skald_vid. The PHP file reads that field, and the skald_vid cookie, by itself. In Node and Python, pass it as vid.
How do I send leads my team made as tests?
Send them with test set to true, for example your team's own accounts or a QA run. Skald keeps and lists them, and never counts them. Leads already sent can be marked test later with the signed leads/test call, or with Mark as test in the dashboard.
My backend is on the same server as Skald, behind Cloudflare. What address do I post to?
Skald's local address, not the public one: Cloudflare can block a server that posts to its own public address. In Docker that is http://127.0.0.1 with the port you published; with the install script, port 8766. The signature is the same either way.
$ tail -f access.log | skald
See what AI does with your site. Free for 30 days.
No card. We set up hosted Skald for you within one working day, or you self-host it free. Every line of your log, sorted and added up.