Skip to content

Commit 2967eeb

Browse files
committed
Update docs
1 parent f7a441e commit 2967eeb

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

docs/news.markdown

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@ Subscribe below to receive updates about improvements and new features on Webhoo
2424
</form>
2525
</div>
2626

27+
## 7 April 2026
28+
29+
* It's now possible to mouseover almost any request data (headers, form data, query strings, etc.) and search for the value instantly.
30+
31+
<figure markdown="span">
32+
![Value Search](https://sf.gl/s/0oX7X.png){ width="700" }
33+
</figure>
34+
35+
* New Database Manager for [Webhook.site Databases](/database.html) that lets you create, modify and delete tables, the table structure, and individual rows.
36+
37+
<figure markdown="span">
38+
![Database Manager](https://sf.gl/s/MyGw6.png){ width="700" }
39+
</figure>
40+
41+
* Added JavaScript AI for easily creating and modifying JavaScript snippets.
42+
43+
<figure markdown="span">
44+
![JavaScript AI](https://sf.gl/s/iONbH.png){ width="700" }
45+
</figure>
46+
47+
* Browser notifications have been improved to show more details about emails and DNSHooks.
48+
* Added `$request.sorting$` variable, a precise timestamp for filtering and sorting.
49+
50+
2751
## 24 Feburary 2026
2852

2953
* New Feature: Alternate Domain - is `webhook.site` blocked on your network? Try the alternate domain toggle, which uses a different IP address and hostname.

docs/webhookscript/examples.markdown

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,3 +597,187 @@ respond('
597597
</p>
598598
'.format(json_encode(array), url))
599599
```
600+
601+
## Converting all JSON data sent to a Webhook.site URL to a CSV file
602+
603+
This script has several moving parts:
604+
605+
* It remembers each time the export runs and only exports the requests that come after the last time the export was invoked
606+
* It uses the Webhook.site API to fetch all requests sent to a Webhook.site URL
607+
* It flattens the JSON data so that each field can become a header/column in the resulting CSV
608+
* If the JSON data has a variable amount of fields ("columns"), this is handled automatically
609+
* The resulting `$csv$` variable can be used directly after, as an attachment, in e.g. a [Send Email](/custom-actions/action-types.html#send-email) action
610+
611+
As an example, the following JSON data structure:
612+
613+
```json title="JSON input data example"
614+
{
615+
"name": "Jack Daniels",
616+
"interests": ["programming", "reading"],
617+
"job": {
618+
"title": "Software Developer",
619+
"company": "Acme Corp"
620+
}
621+
}
622+
```
623+
...becomes:
624+
```csv title="CSV output example"
625+
name,interests.0,interests.1,job.title,job.company
626+
"Jack Daniels","programming","reading","Software Developer","Acme Corp"
627+
```
628+
629+
630+
<br>
631+
632+
```javascript
633+
set('api_key', '00000000-0000-0000-0000-000000000000');
634+
set('token_id', '10000000-0000-0000-0000-000000000000');
635+
636+
// Should run the export from last date + 1 second (to avoid duplicates)
637+
set('from', date_format(get('tracking-export-latest', '1990-01-01 00:00:00') + ' +1 second', 'YYYY-MM-DD HH:mm:ss'))
638+
set('now', date_format('now', 'YYYY-MM-DD HH:mm:ss') )
639+
640+
json_data = []
641+
is_last_page = false
642+
current_page = 1
643+
count = 0
644+
645+
while(!is_last_page and current_page < 100) {
646+
echo('Current page: %d'.format(current_page))
647+
648+
api_url = string_format(
649+
'https://webhook.site/token/{}/requests?query=method:POST AND created_at:["{}" TO *]&sorting=oldest&per_page=50&page={}',
650+
get('token_id'),
651+
get('from'),
652+
current_page
653+
);
654+
655+
res = http(
656+
api_url,
657+
[
658+
'method': 'GET',
659+
'headers': ['Api-Key: ' + get('api_key')]
660+
])
661+
662+
echo('HTTP Status: %s'.format(res['status']))
663+
664+
data = res['content'].json_decode();
665+
666+
if (is_null(data)) {
667+
echo('Could not parse json!');
668+
stop();
669+
break;
670+
}
671+
672+
echo('Total: %s'.format(data['total']))
673+
674+
is_last_page = data['is_last_page']
675+
current_page = current_page + 1
676+
677+
for (item in data['data']) {
678+
// Collect latest date
679+
latest = item['created_at'];
680+
681+
item_data = item['content'].json_decode();
682+
683+
if (is_null(item_data)) {
684+
echo('Could not parse JSON!')
685+
stop()
686+
break;
687+
}
688+
689+
json_data.push(item_data)
690+
}
691+
}
692+
693+
// Handle empty strings, commas and escape quotes - used in next for loop
694+
function csv_escape(input) {
695+
if (input.is_null()) { return ''; }
696+
return '"' + string_replace(input.to_string(), '"', '""') + '"';
697+
}
698+
699+
// Calculate columns
700+
// Not all JSON documents have all columns. Some have a variable amount,
701+
// so we need to loop over the rows once to get the complete set
702+
// of column header names.
703+
704+
csv_columns = []
705+
csv_values = [];
706+
707+
for (row in json_data) {
708+
csv_row = [];
709+
710+
// Flatten JSON structure using Extract JSON action
711+
extract = action('auto_json', ['source': json_encode(row)])
712+
713+
// Collect all flattened names for use in header
714+
// + all rows with correct flattened names as keys
715+
for (var_name in extract.keys()) {
716+
if (var_name.contains('json.')) {
717+
var_name_pretty = var_name.replace('json.', '')
718+
csv_columns[var_name_pretty] = true;
719+
csv_row[var_name_pretty] = csv_escape(extract[var_name])
720+
}
721+
}
722+
723+
csv_values.push(csv_row)
724+
}
725+
726+
// Sort column headers and insert first header line
727+
csv_columns = csv_columns.keys().sort();
728+
csv = csv_columns.join(',')
729+
730+
// Insert rows, but use empty value if the column don't exist
731+
for (csv_val in csv_values) {
732+
csv_row = [];
733+
for (csv_column_name in csv_columns) {
734+
if (csv_val.has(csv_column_name)) {
735+
csv_row[csv_column_name] = csv_val[csv_column_name];
736+
} else {
737+
csv_row[csv_column_name] = '';
738+
}
739+
740+
}
741+
742+
// Append row to CSV
743+
csv = csv + "\n" + csv_row.join(',')
744+
745+
count = count + 1;
746+
}
747+
748+
if (count == 0) {
749+
echo('Nothing processed. Stopping.')
750+
stop()
751+
}
752+
753+
echo('Created CSV contents (%d rows)'.format(count))
754+
755+
// $csv$ and $count$ can be used in downstream actions, e.g. Send Email
756+
set('csv', csv)
757+
set('count', count)
758+
759+
// Store latest seen date for use next time
760+
store('tracking-export-latest', latest)
761+
```
762+
763+
<br>
764+
765+
Additionally, you can use the following script to clean up/delete the data that was converted to CSV - we'd recommend adding it as a separate Script action and gate it with a Condition (for example with a query string comparison, `?delete=yes`) so no data is lost by accident.
766+
767+
```javascript
768+
// Delete CSV-processed data
769+
api_url = string_format(
770+
'https://webhook.site/token/{}/requests?query=method:POST AND created_at:["{}" TO "{}"]',
771+
get('token_id'),
772+
get('from'),
773+
get('now')
774+
)
775+
776+
res = http(
777+
api_url,
778+
[
779+
'method': 'DELETE',
780+
'headers': ['Api-Key: ' + get('api_key')]
781+
]
782+
)
783+
```

0 commit comments

Comments
 (0)