diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..882005e --- /dev/null +++ b/404.html @@ -0,0 +1,941 @@ + + + +
+ + + + + + + + + + + + + +The Webhook.site API is public, free to use, doesn't require authentication and is relatively easy to use.
+Base URL: https://webhook.site
.
You must set the Accept
and Content-Type
headers to application/json
.
:
(colon) to show which parameters must be changed by the user. You must not include this character in the URL.https://webhook.site/00000000-0000-0000-0000-000000000000
, the Token ID is then 00000000-0000-0000-0000-000000000000
.Api-Key
HTTP header.While many endpoints of the Webhook.site API are public and work without any authentication, some endpoints do require authentication, or will return a 401 Unauthorized
status code.
An API Key can be generated in the Control Panel, and provides access to Tokens that are either a) password protected or b) require login.
+API Keys must be specified in the Api-Key
HTTP header.
If you have set a password on a Webhook.site URL/token, to access the API resources for that token, you can use either of the following methods:
+password
query string: ?password=[your password]
To retrieve the data that's sent to a Webhook.site URL or Email, you'll want to use the Get Requests endpoint.
+To create a new token programmatically, you can use the API like this:
+ +This will return information about the token in JSON format, including its UUID. Your URL will be available at the endpoint https://webhook.site/[token uuid]
.
If you are a Webhook.site Pro or Enterprise customer, you should provide an API key in order to associate the created token with your account automatically:
+ + + +The following is a list of the API names for Action Types, along with a list of parameters, and their validation requirements.
+auto_json
¶source
: stringjsonpath
: stringvariable_name
: required, stringaws_cf_invalidate
¶provider_id
: required, intdistribution_id
: required, stringpaths
: required, stringaws_s3_create_bucket
¶provider_id
: string, requiredregion
: string, requiredbucket_name
: string, requiredcanned_acl
: string, in:private,public-read,public-read-write,authenticated-readaws_s3_delete_object
¶provider_id
: string, requiredregion
: stringbucket_name
: string, requiredobject_key
: string, requiredaws_s3_get_object
¶provider_id
: string, requiredregion
: stringbucket_name
: string, requiredobject_key
: string, requiredvariable_name
: string, required, min:1aws_s3_put_object
¶provider_id
: string, requiredregion
: string, requiredbucket_name
: stringobject_key
: string, requiredbody
: string, requiredcanned_acl
: string, in:private,public-read,public-read-write,authenticated-readcondition
¶input
: stringoperator
: required, string, in:eq,neq,sw,ew,ct,nct,gt,gte,lt,ltevalue
: stringaction
: required, string, in:stop,continue,noopconditions
¶conditions
: required, arraymode
: required, string, in:one,all,noneaction
: required, string, in:stop,continue,noopdatabase
¶type
: required, in:mysql,pgsqlhost
: required, stringport
: number, min:1, max:65535database
: required, stringpassword
: stringusername
: required, stringstatement
: required, stringparams
: arrayvariable_name
: stringcharset
: stringdiscord_send_message
¶provider_id
: required, stringcontent
: required, stringusername
: stringavatar_url
: urlembed_type
: string, in:link,image,videoembed_url
: urldont_save
¶No parameters for dont_save
.
dropbox_create_folder
¶provider_id
: string, requiredpath
: string, requireddropbox_delete
¶provider_id
: string, requiredpath
: string, requireddropbox_download_file
¶provider_id
: string, requiredpath
: string, requiredvariable_name
: string, requireddropbox_get_link
¶provider_id
: string, requiredpath
: string, requiredvariable_name
: string, requireddropbox_upload_file
¶provider_id
: string, requiredpath
: string, requiredbody
: string, requiredmode
: string, required, in:add,overwrite,updateextract_jsonpath
¶jsonpath
: required, stringvariable_name
: required, stringsource
: stringdefault
: stringrepeat
: booleanextract_regex
¶regex
: required, stringvariable_name
: required, stringsource
: stringdefault
: stringrepeat
: booleanextract_xpath
¶xpath
: required, stringvariable_name
: required, stringsource
: stringdefault
: stringftp_upload
¶host
: required, stringport
: number, min:1, max:65535password
: required, stringusername
: required, stringpath
: required, stringcontent
: required, stringssl
: boolpassive
: boolgoogle_sheets_add_row
¶provider_id
: string, requiredspreadsheet_id
: string, requiredrange
: string, requiredvalues
: string, requiredformula_mode
: boolgoogle_sheets_get_values
¶provider_id
: string, requiredspreadsheet_id
: string, requiredrange
: string, requiredvariable_name
: required, stringgoogle_sheets_update_row
¶provider_id
: string, requiredspreadsheet_id
: string, requiredrange
: string, requiredvalues
: string, requiredformula_mode
: boolhttp
¶url
: required, stringcontent
: nullable, stringmethod
: nullable, in:POST,GET,OPTIONS,PUT,DELETE,PATCH,TRACEmode
: nullable, in:text,json,multipart,urlencoded,forwardauth
: nullable, objectauth.mode
: string, in:basic,digest,ntlmauth.username
: stringauth.password
: stringmultipart
: array, required_if:mode,multipartmultipart.*.name
: stringmultipart.*.filename
: stringmultipart.*.content-type
: stringmultipart.*.content
: stringurlencoded
: arrayurlencoded.*.name
: string, required_if:mode,urlencodedurlencoded.*.value
: string, required_if:mode,urlencodedheaders
: nullable, stringskip_ssl_verification
: nullable, boolvariable_name
: stringtimeout
: nullable, numeric, max:15image_resize
¶source
: string, requiredwidth
: string, required_without:heightheight
: string, required_without:widthaspect_ratio
: bool, requiredvariable_name
: string, requiredjavascript
¶script
: required, stringlog
¶text
: required, stringmicrosoft_drive_download
¶provider_id
: required, stringpath
: required, stringvariable_name
: stringmicrosoft_drive_upload
¶provider_id
: required, stringpath
: required, stringcontent_type
: stringcontent
: stringvariable_name
: stringmicrosoft_excel_add_rows
¶provider_id
: required, stringpath
: stringtable
: stringindex
: intvalues
: required, arraymicrosoft_excel_get_values
¶provider_id
: required, stringpath
: required, stringworksheet
: required, stringrange
: required, stringvariable_name
: required, stringmodify_response
¶content
: stringstatus
: stringheaders
: stringstop
: boolntfy
¶topic
: string, requiredtitle
: stringmessage
: string, requiredpushed_send
¶app_key
: string, requiredapp_secret
: string, requiredtarget_type
: string, requiredtarget
: string, requiredmessage
: string, requiredrabbitmq_get
¶host
: string, requiredport
: intusername
: string, requiredpassword
: string, requiredvhost
: stringqueue
: string, requiredssl
: booleanvariable_name
: stringrabbitmq_publish
¶host
: string, requiredport
: intusername
: string, requiredpassword
: string, requiredvhost
: stringqueue
: string, requiredssl
: booleanmessage
: string, requiredrate_limit
¶period
: required, intcount
: required, intkey
: stringscript
¶script
: required, stringsend_email
¶sender
: stringrecipient
: required, stringcontent
: stringis_html
: booleansubject
: required, stringattachments
: arraysend_email_smtp
¶sender_name
: stringsender_email
: stringrecipient
: required, stringcontent
: stringis_html
: booleansubject
: required, stringencryption
: string, in:none,ssl,tlsport
: intusername
: string, requiredpassword
: string, requiredhost
: string_requiredattachments
: arraysend_request
¶url
: required, stringcontent
: nullable, stringmethod
: nullable, in:POST,GET,OPTIONS,PUT,DELETEheaders
: nullable, stringskip_ssl_verification
: nullable, boolvariable_name
: stringtimeout
: nullable, numeric, max:30set_variable
¶name
: required, stringvalue
: nullable, stringsftp_upload
¶provider_id
: stringhost
: required, stringport
: number, min:1, max:65535username
: required, stringpassword
: stringpath
: required, stringcontent
: required, stringslack_send_message
¶webhook_url
: required, urlraw
: boolcontent
: required, stringssh_run_command
¶provider_id
: stringhost
: required, stringport
: number, min:1, max:65535username
: required, stringpassword
: stringcommand
: required, stringvariable_name
: stringstop
¶No parameters for stop
.
store_global_variable
¶name
: required, stringvalue
: nullable, stringtemplate
¶template_id
: required, intvariables
: arraytext_map
¶source
: required, stringoperator
: required, stringvariable_name
: required, stringdefault
: required, stringmappings
: required, arraytext_replace
¶source
: required, stringvariable_name
: required, stringreplacements
: required, arraytext_split
¶delimiter
: required, stringsource
: required, stringvariable_name
: required, stringrepeat
: booleantwitter_tweet
¶provider_id
: required, stringtweet
: required, stringThe Custom Actions API allows you to manage the Custom Actions associated with a given Token. More info about Custom Actions.
+List of the API names and parameters for Action Types.
+POST /token/:token_id/actions
type
(string) is the name of an Action Type.order
(int) specified which order the action is executed in.parameters
(object) can vary depending on the Action Type. disabled
(bool) if set to true, the action is skipped upon execution.queue
(bool) If set to true, the action is run asynchronously. More info heredelay
(int, max 86400) If set (along with queue
to true), Webhook.site waits the specified amount of seconds to run the action.condition
(uuid) If set to an ID of a Conditions action, the action will only run if the condition passes, and is otherwise skipped.{
+ "type": "condition",
+ "order": 3,
+ "disabled": false,
+ "parameters": {
+ "input": "$request.content$",
+ "operator": "eq",
+ "value": "",
+ "action": "stop"
+ }
+}
+
{
+ "type": "script",
+ "order": 1,
+ "parameters": {
+ "script": "expiry = '2021-08-01T00:00:00.000000Z'\nnow = to_date('now')\n\nif (date_interval(now, expiry) < 0) {\n // Respond with 410 Gone\n respond('This content is no longer available.', 410)\n}\n"
+ }
+}
+
Same script as Example 2. Requires the requests
module, which can be installed using pip install requests
.
import requests
+
+script = """
+expiry = '2021-08-01T00:00:00.000000Z'
+now = to_date('now')
+
+if (date_interval(now, expiry) < 0) {
+ // Respond with 410 Gone
+ respond('This content is no longer available.', 410)
+}
+"""
+
+data = {
+ "type": "script",
+ "order": 1,
+ "parameters": {
+ "script": script
+ }
+}
+
+r = requests.post('https://webhook.site/token/7d63959e-4fec-49bd-90dc-a4615722825e/actions', json=data)
+
{
+ "uuid": "7ae324d6-c65b-416b-8f83-18fb89e0c740",
+ "token_id": "fe18d303-631d-4620-acb3-5c0b1b0b876d",
+ "type": "condition",
+ "order": 3,
+ "disabled": null,
+ "parameters": {
+ "input": "$request.content$",
+ "operator": "eq",
+ "value": "",
+ "action": "stop"
+ }
+}
+
GET /token/:token_id/actions
200 OK
{
+ "data": [
+ {
+ "uuid": "52055928-099a-44dc-ba31-e8d808b98ea1",
+ "token_id": "fe18d303-631d-4620-acb3-5c0b1b0b876d",
+ "type": "condition",
+ "order": 1,
+ "disabled": false,
+ "parameters": {
+ "input": "$request.header.content-type$",
+ "operator": "nct",
+ "value": "application/json",
+ "action": "stop"
+ }
+ },
+ {
+ "uuid": "27b07ca7-ea83-48f5-b376-2372cf25d3a1",
+ "token_id": "fe18d303-631d-4620-acb3-5c0b1b0b876d",
+ "type": "condition",
+ "order": 2,
+ "disabled": null,
+ "parameters": {
+ "input": "$request.content$",
+ "operator": "eq",
+ "value": "",
+ "action": "stop"
+ }
+ }
+ ]
+}
+
PUT /token/:token_id/actions/:action_id
See Create Custom Action endpoint.
+See Create Custom Action endpoint.
+POST /token/:token_id/test-action
request_id
: A request ID to base the test run on. If not set, uses default request variables.action_id
: When set, overwrites the parameters of an existing action. If not, tests a temporary new, empty action with ID 00000000-0000-4000-0000-000000000000
.200 OK
{
+ "success": true,
+ "result": {
+ "output": {
+ "08529a4f-ad84-450b-977a-1d126d6ca6b7": [
+ "Set runtime variable $aaa$ to \"example\""
+ ],
+ "00000000-0000-4000-0000-000000000000": [
+ "hello world"
+ ]
+ },
+ "response": {
+ "content": null,
+ "status": null,
+ "headers": null
+ },
+ "variables": {
+ "request.header.content-length": "57362",
+ "request.header.user-agent": "Paw/3.3.5 (Macintosh; OS X/11.6.2) GCDHTTPRequest",
+ "request.header.connection": "close",
+ "request.header.host": "webhook.site",
+ "request.header.content-type": "application/json",
+ "request.uuid": "87240a26-1426-45dd-9b4c-961a323652a9",
+ "request.token_id": "7fc77812-9efe-41b6-9365-e2c1fb5feb62",
+ "request.content": "",
+ "request.date": "2022-03-20 10:18:58",
+ "request.timestamp": 1647771538,
+ "request.hostname": "webhook.site",
+ "request.size": 0,
+ "request.type": "web",
+ "request.ip": "86.52.35.76",
+ "request.user_agent": "Paw/3.3.5 (Macintosh; OS X/11.6.2) GCDHTTPRequest",
+ "request.url": "https://webhook.site/7fc77812-9efe-41b6-9365-e2c1fb5feb62",
+ "request.method": "POST",
+ "aaa": "example"
+ }
+ }
+}
+
DELETE /token/:token_id/actions/:action_id
PUT /token/:token_id/actions/toggle
This endpoint toggles whether actions are enabled on a specific token.
+200 OK
Date expressions can be used in the query
parameter when retreiving and filtering Requests from the API.
The expression starts with an anchor date, which can either be now
(or *
), or a date string ending with ||
(two pipe characters).
This anchor date can optionally be followed by one or more maths expressions:
++1h
– Add one hour-1d
– Subtract one day/d
– Round down to the nearest dayThe supported time units differ from those supported by time units for durations. The supported units are:
+Symbol | +Meaning | +
---|---|
y | +Years | +
M | +Months | +
w | +Weeks | +
d | +Days | +
h | +Hours | +
H | +Hours | +
m | +Minutes | +
s | +Seconds | +
Assuming now is 2001-01-01 12:00:00, some examples are:
+Example | +Resolves to | +
---|---|
now+1h | +now in milliseconds plus one hour. Resolves to: 2001-01-01 13:00:00 | +
now-1h | +now in milliseconds minus one hour. Resolves to: 2001-01-01 11:00:00 | +
now-1h/d | +now in milliseconds minus one hour, rounded down to UTC 00:00. Resolves to: 2001-01-01 00:00:00 | +
2001.02.01||+1M/d | +2001-02-01 in milliseconds plus one month. Resolves to: 2001-03-01 00:00:00 | +
If you have a Webhook.site account, before using the API, please first create an API key here. The examples below can also be used without an account, but in that case you should remove the API key header.
+Don't have an account yet? Sign up here.
+curl -X POST 'https://webhook.site/00000000-0000-0000-0000-000000000000' \
+ -H 'content-type: application/json' \
+ -d $'{"id": 7, "name": "Jack Daniels", "position": "Assistant"}'
+
curl 'https://webhook.site/token/00000000-0000-0000-0000-000000000000/request/latest/raw' \
+ -H 'accept: application/json' \
+ -H 'api-key: 00000000-0000-0000-0000-000000000000'
+
Output:
+ +Uploads the file example.png
from the current directory.
To download the file, click the Download link in the Webhook.site interface, or via the API, use the download endpoint.
+Requires the requests
module, which can be installed using pip install requests
.
Prints the 50 latest requests sent to a given URL to console.
+import requests
+
+token_id = "00000000-0000-0000-0000-000000000000"
+headers = {"api-key": "00000000-0000-0000-0000-000000000000"}
+
+r = requests.get('https://webhook.site/token/'+ token_id +'/requests?sorting=newest', headers=headers)
+
+for request in r.json()['data']:
+ print(request)
+
Requires the requests
module, which can be installed using pip install requests
.
You'll also need to replace the API key.
+import requests
+
+json = {
+ "default_status": 200,
+ "default_content": "Hello world!",
+ "default_content_type": "text/html",
+}
+
+headers = {
+ "api-key": "00000000-0000-0000-0000-000000000000"
+}
+
+r = requests.post('https://webhook.site/token', json=json, headers=headers)
+
+print('URL Created: https://webhook.site/' + r.json()['uuid'])
+
Creates a Webhook.site Token and outputs its Web URL. You'll need to replace the API key.
+<?php
+$apiKey = '00000000-0000-0000-0000-000000000000';
+
+// Create a stream context
+$context = stream_context_create(['http' => [
+ 'method' => 'POST',
+ 'header' => "Api-Key: $apiKey\r\n"
+]]);
+
+// Send API request
+$response = json_decode(file_get_contents('https://webhook.site/token', false, $context), true);
+
+echo "URL Created: https://webhook.site/{$response['uuid']}";
+
Simple example of how to loop through the latest requests or emails sent to a Webhook.site URL or email and display in a friendly manner.
+You'll need to replace the API key (if you have a Webhook.site account, otherwise leave it out) and token ID.
+<?php
+$apiKey = '00000000-0000-0000-0000-000000000000';
+$tokenId = '00000000-0000-0000-0000-000000000000';
+
+$url = "https://webhook.site/token/$tokenId/requests?sorting=newest";
+
+$context = stream_context_create(['http' => ['header' => "Api-Key: $apiKey\r\n"]]);
+
+$response = json_decode(file_get_contents($url, false, $context), true);
+
+foreach ($response['data'] as $req) {
+ echo "\n";
+ echo json_encode([
+ 'method' => $req['method'],
+ 'body' => $req['content'],
+ 'date' => $req['created_at'],
+ ], \JSON_PRETTY_PRINT);
+}
+
Example output when running the script after running e.g. curl -X POST https://webhook.site/00000000-0000-0000-0000-000000000000 -d "Hello world"
:
{
+ "method": "POST",
+ "body": "Hello world",
+ "date": "2023-06-08 13:04:33"
+}
+{
+ "method": "POST",
+ "body": "just testing",
+ "date": "2023-06-08 13:03:57"
+}
+
Per default, this script saves the body content of all requests or emails sent to a URL as .json files in the /requests
directory relative to the script file location.
<?php
+$directory = __DIR__ . '/requests';
+//$apiKey = '00000000-0000-0000-0000-000000000000';
+$tokenId = '00000000-0000-0000-0000-000000000000';
+
+if (!is_dir($directory) || !is_writable($directory)) {
+ throw new Exception(sprintf('%s not writable, directory missing or rights issue.', $directory));
+}
+
+$context = stream_context_create(['http' =>
+ isset($apiKey) ? ['header' => "Api-Key: $apiKey\r\n"] : [],
+]);
+
+$page = 1;
+
+do {
+ $url = sprintf('https://webhook.site/token/%s/requests?sorting=newest&page=%d', $tokenId, $page);
+ $response = json_decode(file_get_contents($url, false, $context), true);
+
+ foreach ($response['data'] as $req) {
+ file_put_contents(
+ sprintf('/%s/%s.json', $directory, $req['uuid']),
+ $req['content']
+ );
+ echo(sprintf(
+ "[Page %3d] Downloaded request %s sent at %s (%d bytes)\n",
+ $page, $req['uuid'], $req['created_at'], $req['size']
+ ));
+ }
+
+ $page++;
+ sleep(1);
+} while (!$response['is_last_page'] && isset($response['data']));
+
This script outputs the data sent to a Webhook.site URL in the last 2 hours.
+To do this, the script uses the query
parameter (more info here) in conjunction with a created_at
filter.
Before running the script, you should install dependencies by running npm install axios moment
.
import axios from 'axios';
+import moment from 'moment';
+
+// Change these!
+const apiKey = '00000000-0000-0000-0000-000000000000';
+const tokenId = '00000000-0000-0000-0000-000000000000';
+
+async function getData(apiKey, tokenId) {
+ let date = moment.utc().subtract(2, 'hours').format('YYYY-MM-DD hh:mm:ss');
+ let dateQuery = `created_at:["${date}" TO "*"]`
+
+ try {
+ const response = await axios.get(`https://webhook.site/token/${tokenId}/requests`, {
+ params: {
+ query: dateQuery,
+ },
+ headers: {
+ 'Api-Key': apiKey,
+ 'Accept': 'application/json',
+ }
+ });
+
+ return response.data;
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+const requests = await getData(apiKey, tokenId);
+
+console.log(`${requests.total} requests found:`);
+
+for (const request of requests.data) {
+ console.log({
+ method: request.method,
+ body: request.content,
+ date: request.created_at,
+ });
+}
+
With Global Variables, you can store data permanently that can be shared between your URLs. Global Variables can be used when creating Custom Actions and in Schedules. More about Global Variables
+After creating, the variable $name$
will be available in Custom Actions.
POST /global-variables
name
(string) The name of the variable.value
(string) The value of the variable.{
+ "id": 598297,
+ "name": "content_type",
+ "team_id": 1,
+ "value": "application\/json",
+ "updated_at": "2024-04-15T11:20:02.000000Z",
+ "created_at": "2024-04-15T11:20:02.000000Z"
+}
+
GET /global-variables
per_page
(int) - amount of requests returned, defaults to 50 (max 100)page
(int) - page number to retrieve (default 1)search
(string) - filter variables by name{
+ "current_page": 1,
+ "data": [
+ {
+ "id": 598297,
+ "user_id": 0,
+ "team_id": 1,
+ "name": "content_type",
+ "value": "application\/json",
+ "created_at": "2024-04-15T11:20:02.000000Z",
+ "updated_at": "2024-04-15T11:20:02.000000Z"
+ },
+ {
+ "id": 598294,
+ "user_id": 0,
+ "team_id": 1,
+ "name": "test",
+ "value": "\ud83e\udd72",
+ "created_at": "2024-04-14T13:21:24.000000Z",
+ "updated_at": "2024-04-14T13:21:24.000000Z"
+ }
+ ],
+ "first_page_url": "https:\/\/webhook.test\/global-variables?page=1",
+ "from": 1,
+ "last_page": 21992,
+ "last_page_url": "https:\/\/webhook.test\/global-variables?page=21992",
+ "links": [
+ {
+ "url": null,
+ "label": "« Previous",
+ "active": false
+ },
+ {
+ "url": "https:\/\/webhook.test\/global-variables?page=1",
+ "label": "1",
+ "active": true
+ },
+ {
+ "url": null,
+ "label": "...",
+ "active": false
+ },
+ {
+ "url": "https:\/\/webhook.test\/global-variables?page=21991",
+ "label": "21991",
+ "active": false
+ },
+ {
+ "url": "https:\/\/webhook.test\/global-variables?page=2",
+ "label": "Next »",
+ "active": false
+ }
+ ],
+ "next_page_url": "https:\/\/webhook.test\/global-variables?page=2",
+ "path": "https:\/\/webhook.test\/global-variables",
+ "per_page": 15,
+ "prev_page_url": null,
+ "to": 15,
+ "total": 329870
+}
+
PUT /global-variables/:globalVariableId
(See Create Global Variable above for request.)
+(See Create Global Variable above for response.)
+DELETE /global-variables/:globalVariableId
204 No content
Webhook.site Groups allows you to organize your Tokens. Each Group is a container for multiple Tokens.
+POST /groups
name
(string) The name of the group.{
+ "id": 3498,
+ "team_id": 12098,
+ "name": "My Group",
+ "updated_at": "2023-04-26 18:43:46",
+ "created_at": "2023-04-26 18:43:46"
+}
+
GET /groups
{
+ "current_page": 1,
+ "data": [
+ {
+ "id": 3498,
+ "team_id": 12098,
+ "name": "My Group",
+ "created_at": "2022-10-26 14:25:11",
+ "updated_at": "2022-10-26 14:25:11"
+ },
+ {
+ "id": 3499,
+ "team_id": 12098,
+ "name": "My nice group",
+ "created_at": "2023-04-26 09:57:40",
+ "updated_at": "2023-04-26 09:57:40"
+ }
+ ],
+ "first_page_url": "https:\/\/webhook.site\/groups?page=1",
+ "from": 1,
+ "last_page": 1,
+ "last_page_url": "https:\/\/webhook.site\/groups?page=1",
+ "next_page_url": null,
+ "path": "https:\/\/webhook.site\/groups",
+ "per_page": "2",
+ "prev_page_url": null,
+ "to": 2,
+ "total": 2
+}
+
PUT /groups/:groupId
(See Create Group above for request.)
+(See Create Group above for response.)
+DELETE /groups/:groupId
204 No content
The Requests API is used to retrieve, manipulate and delete data sent to a given Webhook.site token (URL, E-mail Address or DNSHook.)
+any method /:tokenId
+any method /:tokenId/:statusCode
+any method /:tokenId/(anything)
If statusCode
is valid, that HTTP status will be used in the response (instead of the default.)
Instead of tokenId
, an the alias of the token can also be supplied. Multiple subpaths, e.g. /:tokenId/api/v1/users
, will also be captured.
If the Token has a timeout
value, there is a dynamic rate limit of 100 ÷ timeout
requests per minute, e.g. a timeout of 30 allows for 3 requests per minute, and 1 second allows for 100 requests per minute.
The default response (or a response set with e.g. the Modify Response Custom Action) of the Token will be returned.
+GET /token/:token_id/requests
Lists all request sent to a token.
+sorting
(string) - either newest
or oldest
(default)per_page
(int) - amount of requests returned, defaults to 50 (max 100)page
(int) - page number to retrieve (default 1)date_from
, date_to
(date string) - filter requests by date, format yyyy-MM-dd HH:mm:ss
query
(string) - filter requests by a query string search (see below for examples)The following fields can be used to filter via the query
parameter:
uuid
token_id
team_id
type
hostname
size
content
time
created_at
updated_at
custom_action_output
files.[id]
headers.[header]
method
- type web
onlyuser_agent
- type web
onlyurl
- type web
onlyip
- type web
onlyquery.[field]
- type web
onlyrequest.[field]
- type web
only (form fields)sender
- type email
onlytext_content
- type email
onlymessage_id
- type email
onlychecks.[type]
- type email
onlydestinations]
- type email
onlyYou can filter requests by the following syntax:
+foobar
- returns requests or emails with body contents containing the word foobar
content:foobar
- returns requests or emails with body contents containing the word foobar
method:GET
- returns all requests with method GETheaders.user-agent:"Paw/3.3.5 (Macintosh; OS X/11.6.2) GCDHTTPRequest"
- search value of user-agent headerquery.action:create
- returns requests that have the query string action
set to create
._exists_:query.action
- returns requests where the action query parameter existstype:web
/ type:email
- returns either Web requests or emailstype:web AND method:POST
- AND querymethod:PUT OR method:POST
- OR query(method:PUT) AND (content:example OR content:test) AND NOT (content:foobar)
created_at:["2022-01-01 00:00:00" TO "2022-12-31 00:00:00"]
- date range querycreated_at:["2022-01-01 00:00:00" TO *]
- date range query (from date until now)created_at:[now-10m TO now]
- date range query (10 minutes ago until now; reference for date expressions)If you're in doubt about where these parameters go in an API request, take a look below. This URL combines a search query via the query
parameter (searching requests containing the word foobar
), as well as the sorting
and per_page
parameters.
https://webhook.site/token/a94a7294-c4aa-4074-ab77-c4cf86fd53b1/requests?query=content:foobar&sorting=newest&per_page=10
{
+ "data": [
+ {
+ "uuid": "a2a6a4ae-4130-4063-953a-84fa29d81d43",
+ "token_id": "a94a7294-c4aa-4074-ab77-c4cf86fd53b1",
+ "ip": "127.0.0.1",
+ "hostname": "webhook.site",
+ "method": "POST",
+ "user_agent": "Paw\/3.1.8 (Macintosh; OS X\/10.14.6) GCDHTTPRequest",
+ "content": "{\"first_name\":\"Arch\",\"last_name\":\"Weber\"}",
+ "query": {
+ "action": "create"
+ },
+ "request": {
+ "status": "example"
+ },
+ "files": {
+ "file": {
+ "id": "98bf4c25-58ab-4c5d-ba91-fb6f709ea78d",
+ "filename": "example.png",
+ "size": 420915,
+ "content_type": "image/png"
+ }
+ },
+ "headers": {
+ "content-length": [
+ "271"
+ ],
+ "user-agent": [
+ "Paw\/3.1.8 (Macintosh; OS X\/10.14.6) GCDHTTPRequest"
+ ],
+ "request-id": [
+ "37856131"
+ ]
+ },
+ "url": "https:\/\/webhook.site\/a94a7294-c4aa-4074-ab77-c4cf86fd53b1\/201?",
+ "created_at": "2019-10-03 19:06:35",
+ "updated_at": "2019-10-03 19:06:35",
+ "custom_action_output": []
+ }
+ ],
+ "total": 1,
+ "per_page": 50,
+ "current_page": 1,
+ "is_last_page": true,
+ "from": 1,
+ "to": 1
+}
+
GET /token/:token_id/request/:request_id
GET /token/:token_id/request/latest
- retrieves the latest request sent to the URL
{
+ "uuid": "a2a6a4ae-4130-4063-953a-84fa29d81d43",
+ "token_id": "a94a7294-c4aa-4074-ab77-c4cf86fd53b1",
+ "ip": "127.0.0.1",
+ "hostname": "webhook.site",
+ "method": "POST",
+ "user_agent": "Paw\/3.1.8 (Macintosh; OS X\/10.14.6) GCDHTTPRequest",
+ "content": "{\"first_name\":\"Arch\",\"last_name\":\"Weber\"}",
+ "query": {
+ "action": "create"
+ },
+ "headers": {
+ "content-length": [
+ "271"
+ ],
+ "user-agent": [
+ "Paw\/3.1.8 (Macintosh; OS X\/10.14.6) GCDHTTPRequest"
+ ]
+ },
+ "files": {
+ "foo": {
+ "id": "65d6e0ce-a840-47bc-b6b6-ff1ff38c34ca",
+ "filename": "example.json",
+ "size": 5132873,
+ "content_type": "text/plain"
+ }
+ },
+ "url": "https:\/\/webhook.site\/a94a7294-c4aa-4074-ab77-c4cf86fd53b1\/201?",
+ "created_at": "2019-10-03 19:06:35",
+ "updated_at": "2019-10-03 19:06:35"
+}
+
GET /token/:token_id/request/:request_id/raw
GET /token/:token_id/request/latest/raw
- retrieves the latest request sent to the URL
Returns the request as a response (body, content-type.)
+200 OK
GET /token/:tokenId/request/:requestId/download/:fileId
Files that are included in a request or as email attachments are available to download using this endpoint.
+304 Redirect
DELETE /token/:token_id/request/:request_id
Deletes a request.
+200 OK
DELETE /token/:token_id/request
Deletes all requests associated with the token, or if query
, date_from
and/or date_to
is specified, only that subset of requests is deleted.
date_from
, date_to
- filter requests by date, format yyyy-MM-dd HH:mm:ss
query
- filter requests by a query string search. See here for examples.200 OK
POST /schedules
name
The name of the schedule.interval
One of the following interval strings: monthly
, weekly
, daily
, hourly
, 10-minute
, 5-minute
, 1-minute
, cron
cron
When interval
is cron
, specify a cron-style interval, e.g. */5 * * * *
for every 5 minutes. Otherwise can be left out.request_url
The request URL that the schedule should act on.request_method
HTTP Method (POST, GET, etc.)request_body
request_headers
HTTP headers, separated by \n
timeout
Timeout in seconds (min 1, max 30)require_body
If specified, Webhook.site sends an error notification if response body doesn't contain this string.require_status_min
If specified, Webhook.site sends an error notification if response status doesn't fit within range.require_status_max
If specified, Webhook.site sends an error notification if response status doesn't fit within range.Variables will be replaced in the fields request_url
, request_method
, request_headers
and request_body
. More info here.
{
+ "name": "My schedule",
+ "interval": "5-minute",
+ "request_url": "https://example.com",
+ "request_method": "POST",
+ "request_body": "{\"json\": \"message\"}",
+ "request_headers": "Authorization: Bearer mytoken\nContent-Type: application/json"
+}
+
{
+ "name": "My schedule",
+ "interval": "5-minute",
+ "request_url": "https:\/\/example.com",
+ "request_method": "POST",
+ "request_body": "{\"json\": \"message\"}",
+ "request_headers": "Authorization: Bearer mytoken\nContent-Type: application\/json",
+ "require_body": null,
+ "require_status_min": null,
+ "require_status_max": null,
+ "user_id": 21,
+ "updated_at": "2021-05-01 13:27:25",
+ "created_at": "2021-05-01 13:27:25",
+ "id": 58
+}
+
GET /schedules?page=1&per_page=15
{
+ "current_page": 1,
+ "data": [
+ {
+ "id": 58,
+ "name": "My schedule",
+ "interval": "5-minute",
+ "cron": null,
+ "user_id": 21,
+ "request_method": "POST",
+ "request_url": "https:\/\/example.com",
+ "request_headers": "Authorization: Bearer mytoken\nContent-Type: application\/json",
+ "request_body": "{\"json\": \"message\"}",
+ "timeout": 5,
+ "require_body": null,
+ "require_status_min": null,
+ "require_status_max": null,
+ "last_run_at": null,
+ "last_status": null,
+ "created_at": "2021-05-01 13:27:25",
+ "updated_at": "2021-05-01 13:27:25"
+ }
+ ],
+ "first_page_url": "https:\/\/webhook.site\/schedules?page=1",
+ "from": 1,
+ "last_page": 1,
+ "last_page_url": "https:\/\/webhook.site\/schedules?page=1",
+ "next_page_url": null,
+ "path": "https:\/\/webhook.site\/schedules",
+ "per_page": 15,
+ "prev_page_url": null,
+ "to": 6,
+ "total": 6
+}
+
GET /schedules/:scheduleId
{
+ "id": 58,
+ "name": "My schedule",
+ "interval": "5-minute",
+ "cron": null,
+ "user_id": 21,
+ "request_method": "POST",
+ "request_url": "https:\/\/example.com",
+ "request_headers": "Authorization: Bearer mytoken\nContent-Type: application\/json",
+ "request_body": "{\"json\": \"message\"}",
+ "timeout": 5,
+ "require_body": null,
+ "require_status_min": null,
+ "require_status_max": null,
+ "last_run_at": null,
+ "last_status": null,
+ "created_at": "2021-05-01 13:27:25",
+ "updated_at": "2021-05-01 13:27:25"
+}
+
PUT /schedules/:scheduleId
(See Create schedule above for request.)
+(See Get single schedule for response.)
+DELETE /schedules/:scheduleId
204 No content
POST /schedules/:scheduleId/run-now
GET /schedules/:scheduleId/logs
Set Accept
header to application/json
.
Rate limit: 60 requests per minute.
+{
+ "data": [
+ {
+ "id": 2,
+ "schedule_id": 1,
+ "response_status": 200,
+ "response_headers": {
+ "Age": [
+ "447707"
+ ],
+ "Cache-Control": [
+ "max-age=604800"
+ ],
+ "Content-Type": [
+ "text\/html; charset=UTF-8"
+ ]
+ },
+ "response_body": "<!doctype html>\n<html>\n<head>...",
+ "error": null,
+ "created_at": "2022-11-06 19:27:09",
+ "updated_at": "2022-11-06 19:27:09"
+ },
+ {
+ "id": 3,
+ "schedule_id": 1,
+ "response_status": 200,
+ "response_headers": {
+ "Age": [
+ "404973"
+ ],
+ "Cache-Control": [
+ "max-age=604800"
+ ],
+ "Content-Type": [
+ "text\/html; charset=UTF-8"
+ ]
+ },
+ "response_body": "<!doctype html>\n<html>\n<head>...",
+ "error": null,
+ "created_at": "2022-11-06 19:30:02",
+ "updated_at": "2022-11-06 19:30:02"
+ }
+ ],
+ "current_page": 1,
+ "first_page_url": "https:\/\/webhook.site\/control-panel\/schedules\/1\/logs?page=1",
+ "from": 1,
+ "last_page": 1,
+ "last_page_url": "https:\/\/webhook.site\/control-panel\/schedules\/1\/logs?page=1",
+ "next_page_url": null,
+ "path": "https:\/\/webhook.site\/control-panel\/schedules\/1\/logs",
+ "per_page": 15,
+ "prev_page_url": null,
+ "to": 2,
+ "total": 2
+}
+
Webhook.site Templates allows you to create templates of actions, and re-use these templates with the Include Template Custom Action.
+POST /template
name
The name of the template.actions
An array of objects containing Custom Actions. All parameters of actions except disabled
can be used. More info herevariables
An array of name-value objects containing predefined variables. These variables are defined before the action runs, and are available to any subsequent actions after the template is included.{
+ "actions": [
+ {
+ "name": null,
+ "type": "conditions",
+ "queue": false,
+ "delay": 0,
+ "condition": null,
+ "parameters": {
+ "conditions": [
+ {
+ "input": "$date$",
+ "operator": "eq",
+ "value": "$post-news-last-date$"
+ }
+ ],
+ "mode": "all",
+ "action": "stop"
+ },
+ "order": 2
+ },
+ {
+ "name": null,
+ "type": "twitter_tweet",
+ "queue": false,
+ "delay": 0,
+ "condition": null,
+ "parameters": {
+ "provider_id": "501133",
+ "tweet": "$tweet$"
+ },
+ "order": 3
+ },
+ {
+ "name": null,
+ "type": "store_global_variable",
+ "queue": false,
+ "delay": 0,
+ "condition": null,
+ "parameters": {
+ "name": "post-news-last-date",
+ "value": "$date$"
+ },
+ "order": 4
+ }
+ ],
+ "name": "My Template",
+ "variables": [
+ {
+ "name": "example",
+ "value": "hello world"
+ }
+ ],
+}
+
{
+ "name": "My Template",
+ "variables": [
+ {
+ "name": "example",
+ "value": "hello world"
+ }
+ ],
+ "team_id": 1,
+ "updated_at": "2024-02-20T10:01:29.000000Z",
+ "created_at": "2024-02-20T10:01:29.000000Z",
+ "id": 21,
+ "actions": [
+ {
+ "condition": null,
+ "delay": 0,
+ "parameters": {
+ "mode": "all",
+ "conditions": [
+ {
+ "input": "$date$",
+ "operator": "eq",
+ "value": "$post-news-last-date$"
+ }
+ ],
+ "action": "stop"
+ },
+ "token_id": "f1bd342e-c98b-4ac4-bf3f-c35f1f338161",
+ "type": "conditions",
+ "order": 2,
+ "name": null,
+ "template_id": 21,
+ "uuid": "9b615454-3c64-4da2-a2f7-d524797a8925",
+ "updated_at": "2024-02-20T10:01:29.000000Z",
+ "created_at": "2024-02-20T10:01:29.000000Z"
+ },
+ {
+ "condition": null,
+ "delay": 0,
+ "parameters": {
+ "tweet": "$tweet$",
+ "provider_id": "501133"
+ },
+ "token_id": "f1bd342e-c98b-4ac4-bf3f-c35f1f338161",
+ "type": "twitter_tweet",
+ "order": 3,
+ "name": null,
+ "template_id": 21,
+ "uuid": "9b615454-3ce7-4c63-bc8b-3412694ae633",
+ "updated_at": "2024-02-20T10:01:29.000000Z",
+ "created_at": "2024-02-20T10:01:29.000000Z"
+ },
+ {
+ "condition": null,
+ "delay": 0,
+ "parameters": {
+ "name": "post-news-last-date",
+ "value": "$date$"
+ },
+ "token_id": "f1bd342e-c98b-4ac4-bf3f-c35f1f338161",
+ "type": "store_global_variable",
+ "order": 4,
+ "name": null,
+ "template_id": 21,
+ "uuid": "9b615454-3d5f-4cd6-bd4b-94b55e6f6f2f",
+ "updated_at": "2024-02-20T10:01:29.000000Z",
+ "created_at": "2024-02-20T10:01:29.000000Z"
+ }
+ ]
+}
+
GET /templates
{
+ "current_page": 1,
+ "data": [
+ {
+ "name": "My Template",
+ "variables": [
+ {
+ "name": "example",
+ "value": "hello world"
+ }
+ ],
+ "team_id": 1,
+ "updated_at": "2024-02-20T10:01:29.000000Z",
+ "created_at": "2024-02-20T10:01:29.000000Z",
+ "id": 21,
+ "actions": [
+ {
+ "condition": null,
+ "delay": 0,
+ "parameters": {
+ "mode": "all",
+ "conditions": [
+ {
+ "input": "$date$",
+ "operator": "eq",
+ "value": "$post-news-last-date$"
+ }
+ ],
+ "action": "stop"
+ },
+ "token_id": "f1bd342e-c98b-4ac4-bf3f-c35f1f338161",
+ "type": "conditions",
+ "order": 2,
+ "name": null,
+ "template_id": 21,
+ "uuid": "9b615454-3c64-4da2-a2f7-d524797a8925",
+ "updated_at": "2024-02-20T10:01:29.000000Z",
+ "created_at": "2024-02-20T10:01:29.000000Z"
+ },
+ {
+ "condition": null,
+ "delay": 0,
+ "parameters": {
+ "tweet": "$tweet$",
+ "provider_id": "501133"
+ },
+ "token_id": "f1bd342e-c98b-4ac4-bf3f-c35f1f338161",
+ "type": "twitter_tweet",
+ "order": 3,
+ "name": null,
+ "template_id": 21,
+ "uuid": "9b615454-3ce7-4c63-bc8b-3412694ae633",
+ "updated_at": "2024-02-20T10:01:29.000000Z",
+ "created_at": "2024-02-20T10:01:29.000000Z"
+ },
+ {
+ "condition": null,
+ "delay": 0,
+ "parameters": {
+ "name": "post-news-last-date",
+ "value": "$date$"
+ },
+ "token_id": "f1bd342e-c98b-4ac4-bf3f-c35f1f338161",
+ "type": "store_global_variable",
+ "order": 4,
+ "name": null,
+ "template_id": 21,
+ "uuid": "9b615454-3d5f-4cd6-bd4b-94b55e6f6f2f",
+ "updated_at": "2024-02-20T10:01:29.000000Z",
+ "created_at": "2024-02-20T10:01:29.000000Z"
+ }
+ ]
+ }
+ ],
+ "first_page_url": "https:\/\/webhook.site\/templates?page=1",
+ "from": 1,
+ "last_page": 1,
+ "last_page_url": "https:\/\/webhook.site\/templates?page=1",
+ "links": [
+ {
+ "url": null,
+ "label": "« Previous",
+ "active": false
+ },
+ {
+ "url": "https:\/\/webhook.site\/templates?page=1",
+ "label": "1",
+ "active": true
+ },
+ {
+ "url": null,
+ "label": "Next »",
+ "active": false
+ }
+ ],
+ "next_page_url": null,
+ "path": "https:\/\/webhook.site\/templates",
+ "per_page": 15,
+ "prev_page_url": null,
+ "to": 1,
+ "total": 1
+}
+
PUT /templates/:templateId
(See Create Template above for request.)
+(See Create Template above for response.)
+DELETE /templates/:templateId
204 No content
A token is a container for incoming requests and emails, and corresponds to a Webhook.site URL or Email. A token ID is a 36 character UUID consisting of hexadecimal characters and dashes.
+Simply, the token ID is the part after https://webhook.site/
in the URL, or before @email.webhook.site
in the email address.
POST /token
After creating a token, the URL at https://webhook.site/{token.uuid}
becomes accessible, and emails can be sent to {token.uuid}@email.webhook.site
.
default_*
parameters sets the response of the URL.timeout
(int) waits an amount of seconds before returning the response, max 30. Intended for testing timeouts, requests to tokens with timeouts are rate limited; a high timeout value will incur a lower rate limit.expiry
(int) amount of seconds until token auto-expiration. Max value (and default for non-upgraded URLs) is 604800 (one week). Intended for e.g. automated testing pipelines. Leave out or set to null
to disable.request_limit
(int) - limits the request history amount from 1 to 10000 (default)cors
(bool) set to true will add CORS headers to the request so browsers will send cross-domain requests to the URLalias
(string) allows setting the alias of the token.actions
(bool) specifies if Custom Actions are enabled and executed on every request/email (true), or disabled (false.)clone_from
(uuid string) specifies a token UUID (or alias) that will act as a template for the new token. When specified, settingssuch as default content, timeout, password as well as Custom Actions are copied to the new token.group_id
(int) specifies which group ID the token should be added to.{
+ "default_status": 200,
+ "default_content": "Hello world!",
+ "default_content_type": "text/html",
+ "timeout": 0,
+ "cors": false,
+ "expiry": 604800,
+ "alias": "my-webhook",
+ "actions": true
+}
+
Requires the requests
module, which can be installed using pip install requests
. You'll also need to replace the API key. Create an API key here.
import requests
+
+json = {
+ "default_status": 200,
+ "default_content": "Hello world!",
+ "default_content_type": "text/html",
+}
+
+headers = {
+ "api-key": "00000000-0000-0000-0000-000000000000"
+}
+
+r = requests.post('https://webhook.site/token', json=json, headers=headers)
+
+print('URL Created: https://webhook.site/' + r.json()['uuid'])
+
200 OK
{
+ "redirect": false,
+ "alias": null,
+ "timeout": 0,
+ "premium": true,
+ "uuid": "9981f9f4-657a-4ebf-be7c-1915bedd4775",
+ "ip": "127.0.0.1",
+ "user_agent": "Paw\/3.1.8 (Macintosh; OS X\/10.14.6) GCDHTTPRequest",
+ "default_content": "Hello world!",
+ "default_status": 200,
+ "default_content_type": "text\/plain",
+ "premium_expires_at": "2019-10-22 10:52:20",
+ "created_at": "2019-09-22 10:52:20",
+ "updated_at": "2019-09-22 10:52:20"
+ "expires_at": "2019-09-29 10:52:20"
+}
+
GET /token
Returns a list of all Tokens associated with an account.
+per_page
- amount of requests returned, defaults to 50 (max 100)page
- page number to retrieve (default 1)order_by
- which field to order tokens by (created_at
(default) or token_id
)order_direction
- order direction (asc
(default) or desc
){
+ "current_page": 1,
+ "data": [
+ {
+ "uuid": "44fb1548-cd1f-4928-880c-cce094e5e179",
+ "redirect": false,
+ "alias": null,
+ "actions": true,
+ "cors": false,
+ "expiry": false,
+ "timeout": 0,
+ "premium": true,
+ "user_id": null,
+ "password": true,
+ "ip": "127.0.0.1",
+ "user_agent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit\/605.1.15 (KHTML, like Gecko) Version\/14.0.3 Safari\/605.1.15",
+ "default_content": "",
+ "default_status": 200,
+ "default_content_type": "text\/plain",
+ "premium_expires_at": null,
+ "created_at": "2021-08-11 18:34:44",
+ "updated_at": "2021-08-11 18:34:44",
+ "latest_request_id": "ea5f5920-0398-465c-8f9c-8074f0d805a4",
+ "latest_request_at": "2021-08-12 19:56:50",
+ "group_id": null,
+ "requests": 1
+ },
+ ...
+ ],
+ "first_page_url": "https:\/\/webhook.site\/token?page=1",
+ "from": 1,
+ "last_page": 1,
+ "last_page_url": "https:\/\/webhook.site\/token?page=1",
+ "next_page_url": null,
+ "path": "https:\/\/webhook.site\/token",
+ "per_page": 50,
+ "prev_page_url": null,
+ "to": 2,
+ "total": 2
+}
+
GET /token/:token_id
PUT /token/:token_id
PUT /token/:token_id/password
Sets a password to view the requests of a token.
+PUT /token/:token_id/alias
Sets the alias for the token, which makes the token available at https://webhook.site/<alias>
or <alias>@email.webhook.site
in addition to its 36 character UUID.
Rules for alias format: Length between 3-32 characters. Allowed characters: A-Z, a-z and - (dash.)
+Attaches CORS headers to the response of the Token, allowing browsers to request it from all domains.
+PUT /token/:token_id/cors/toggle
DELETE /token/:token_id
204 No Content
The Webhook.site Command Line Interface listens for requests to a Webhook.site URL and forwards/redirects them to your local computer or server.
+ + + + +After installing Docker, you can run Webhook.site CLI via docker run
:
docker run -ti webhooksite/cli -- whcli help
Node version 14 or greater required.
+To install: npm install -g @webhooksite/cli
Then you can run e.g. whcli help
Some command arguments can be specified via environment variables:
+ --token WH_TOKEN
+ --api-key WH_API_KEY
+ --target WH_TARGET
+ --command WH_COMMAND
+
General environment variables:
+WH_LOG_LEVEL
Sets log level (silent, trace, debug, info, warn, error, fatal.) Defaults to info.
For some commands and arguments, runtime variables can be replaced with the standard Webhook.site Custom Actions syntax (e.g. $variable$). Global Variables are not replaced.
+The variables that are available are the default base variables and any variables defined in a Custom Action that was run during the request.
+help
: List commands¶Lists commands available to the CLI.
+forward
: Forward requests¶The forward
command listens for new incoming requests sent to your Webhook.site URL and immediately relays them to any URL you specify, or simply localhost
(so it can be used as an ngrok alternative). This URL can be any URL that the machine running Webhook.site CLI can access.
--token
) parameter must specify the Webhook.site URL ID (also called token ID). The token ID is the long 36-character ID at the end of your Webhook.site URL.--api-key
) must also be specified when the token belongs to a Webhook.site account, and can be generated from the Webhook.site Control Panel.--target
) specifies where traffic should be redirected (defaults to https://localhost
)whcli forward \
+ --token=1e25c1cb-e4d4-4399-a267-cd2cf1a6c864 \
+ --api-key=ef6ef2f8-3e48-4f77-a54c-3891dc11c05c \
+ --target=https://example.com
+
Custom Action variables are replaced in the --target
argument.
The request method, headers and any additional path or query string parameters added to the Webhook.site URL is forwarded on to the target. For example, if the target URL is https://example.com
, sending a POST request to https://webhook.site/c33f3c3e-6018-4634-b406-65338edee460/example?query=value
, the target URL will also receive a POST request on https://example.com/example?query=value
.
When running Webhook.site CLI in Docker, to access the host machine, you can't use localhost
or 127.0.0.1. Instead, use the special hostname host.docker.internal
. More info here.
If the target uses a self-signed certificate, you could get an error message about this. To allow self-signed certs, run whcli
with the following environment variable and value:
exec
: Execute commands¶Allows executing terminal commands for new incoming requests sent to your Webhook.site URL. Custom Action variables are replaced in the --command
argument.
whcli exec \
+ --token=1e25c1cb-e4d4-4399-a267-cd2cf1a6c864 \
+ --api-key=ef6ef2f8-3e48-4f77-a54c-3891dc11c05c \
+ --command='ping $request.ip$'
+
With Custom Actions, it is possible to create a workflow out of a set of actions that are executed whenever a Webhook.site URL receives a request or email.
+Using this functionality, you can connect APIs that aren't compatible, convert a HTTP request to an email or vice versa, build workflows that would otherwise require a developer, and much, much more.
+In this video I'm doing a quick demo of how I used Webhook.site to set up a workflow to post updates from my news page automatically to my X/Twitter account. I used the actions JavaScript, Conditions, X Post, and Store Global Variable.
+In the following demo, webshop order details are received in a webhook. We then use Extract JSONPath and Google Sheets actions to insert their name in a Google Sheet. It also shows how variables interact with downstream actions.
+If you use the same set of Custom Actions often, you can create a Template that contains a copy of one or more Custom Actions, along with a set of Predefined Variables.
+You can then use this template by creating an Import Template action.
+To create a Template, click the Create Template button at the bottom of the Custom Actions overlay. Then enter a name for the template and select which actions should be copied to the template. If your template actions e.g. depend on variables pre-existing, like configuration data, you can add one or more Predefined Variables. These are available to the actions in the Template, but also to any subsequent actions coming after the Import Template action.
+Predefined Variables can be updated in the Control Panel.
+Changes to the actions that the Template was created from do not automatically carry over to the template. Instead, Templates can be updated post-creation by overwriting them with a new set of actions.
+Templates can also be managed using the Template API.
+Webhook.site allows some action types to be repeating, which makes Webhook.site "loop over" one or more values without needing to use scripts.
+Currently, repetition is only supported by the Extract JSONPath and Extract Regex action types.
+Currently, the maximum amount of items that are supported is 100 to prevent abuse. This limit may be raised in the future. Items above that are ignored.
+The repeating action should be ordered before the actions that are to be repeated. The actions that are repeated will run for each item that is extracted, using the same variable name.
+By checking the Queued checkbox when creating a Custom Action, Webhook.site will run that specific action in a background queue (asynchronously).
+This is useful when you need your Webhook.site URL to respond quickly, but your Custom Actions are taking a long time to run. For example, if your Webhook.site URL should respond in 5 seconds, but you need to call an endpoint with a HTTP Request action that responds in 10 seconds, you can queue the HTTP Request action.
+Additionally, you can specify an amount of seconds to wait until the action is executed. To do this, enter an amount of seconds in the Seconds textbox next to the Queue checkbox.
+As the a queued action will inherit the execution scope up until the action, there are a few things to be aware of when using Queued Actions:
+Executes custom scripts using a scripting language that's very similar to JavaScript and PHP. More information here
+This action runs a JSONPath query on the contents of a request. With it, you can extract any data from a JSON document and store it in a variable, which can then be used in a downstream action.
+JSONPath is very similar to the jq
commandline utility.
Example data:
+{
+ "store": {
+ "name": "Cool Books Ltd",
+ "books": [
+ {
+ "title": "12 Rules for Life",
+ "author": "Jordan B. Peterson",
+ "author.age": 60,
+ "price": 10,
+ "isbn": "13123123123"
+ },
+ {
+ "title": "How to Win Friends and Influence People",
+ "author": "Dale Carnegie",
+ "price": 9,
+ "isbn": "23482394"
+ }
+ ]
+ }
+}
+
JSONPath | +Result | +
---|---|
.store.name |
+name property of store object |
+
.store.books[0]["author.age"] |
+author age of first book (bracket syntax can be useful for e.g. keys containing periods) | +
$.store.books[*].author |
+the authors of all books in the store | +
$..author |
+all authors | +
$.store..price |
+the price of everything in the store. | +
$..books[2] |
+the third book | +
$..books[(@.length-1)] |
+the last book in order. | +
$..books[-1:] |
+the last book in order. | +
$..books[0,1] |
+the first two books | +
$..books[:2] |
+the first two books | +
$..books[::2] |
+every second book starting from first one | +
$..books[1:6:3] |
+every third book starting from 1 till 6 | +
$..books[?(@.isbn)] |
+filter all books with isbn number property | +
$..books[?(@.isbn != '')] |
+filter all books with isbn that isn't null or "" (empty string.) (Bracket syntax can also be used here, e.g. .values[?(@['my value'] != '')] ) |
+
$..books[?(@.price<10)] |
+filter all books cheaper than 10 | +
$..* |
+all elements in the data (recursively extracted) | +
Symbol | +Description | +
---|---|
$ |
+The root object/element (not strictly necessary) | +
@ |
+The current object/element | +
. or [] |
+Child operator | +
.. |
+Recursive descent | +
* |
+Wildcard. All child elements regardless their index. | +
[,] |
+Array indices as a set | +
[start:end:step] |
+Array slice operator borrowed from ES4/Python. | +
?() |
+Filters a result set by a script expression | +
() |
+Uses the result of a "script" expression as the index | +
For more details on what's possible with JSONPath, take a look at the docs.
+As you start entering a JSONPath, the results are validated and shown next to the input field.
+This action automatically converts JSON data to Webhook.site Variables, and can be used as an alternative for Extract JSONPath when there's a large amount of variables that need to be extracted.
+Per default, the action works on the JSON found in the $request.content$
variable, e.g. the request body data.
If the JSONPath parameter is specified, this can be used to limit the variable creation to only the subset of data specified by the JSONPath query.
+If the following data is specified in the Source parameter:
+{
+ "Actors": [
+ {
+ "name": "Tom Cruise",
+ "age": 56,
+ "Born At": "Syracuse, NY",
+ "Birthdate": "July 3, 1962",
+ "photo": "https://jsonformatter.org/img/tom-cruise.jpg"
+ },
+ {
+ "name": "Robert Downey Jr.",
+ "age": 53,
+ "Born At": "New York City, NY",
+ "Birthdate": "April 4, 1965",
+ "photo": "https://jsonformatter.org/img/Robert-Downey-Jr.jpg"
+ }
+ ]
+}
+
If the JSONPath parameter is empty, the following 10 variables will be created:
+Variable Name | +Value | +
---|---|
$json.Actors.0.name$ |
+Tom Cruise | +
$json.Actors.0.age$ |
+56 | +
$json.Actors.0.Born At$ |
+Syracuse, NY | +
$json.Actors.0.Birthdate$ |
+July 3, 1962 | +
$json.Actors.0.photo$ |
+https://example.com/tom-cruise.jpg | +
$json.Actors.1.name$ |
+Robert Downey Jr. | +
$json.Actors.1.age$ |
+53 | +
$json.Actors.1.Born At$ |
+New York City, NY | +
$json.Actors.1.Birthdate$ |
+April 4, 1965 | +
$json.Actors.1.photo$ |
+https://example.com/Robert-Downey-Jr.jpg | +
If the JSONPath parameter is set to .Actors.0
, only the following 5 variables are created:
Variable Name | +Value | +
---|---|
$json.0.name$ |
+Tom Cruise | +
$json.0.age$ |
+56 | +
$json.0.Born At$ |
+Syracuse, NY | +
$json.0.Birthdate$ |
+July 3, 1962 | +
$json.0.photo$ |
+https://example.com/tom-cruise.jpg | +
This action runs a Regex (regular expression) query on the contents of a request. With it, you can extract any data from a text document and store it in a variable, which can then be used in a downstream action.
+As you start entering a Regex, the results are validated and shown next to the input field.
+Similar to the Extract JSONPath Custom Action, Extract XPath lets you extract values from an XML or HTML document and save the result as a variable.
+The following examples are based on this XML document:
+<?xml version="1.0"?>
+<organization name="ExampleCo">
+ <employees>
+ <employee id="1">Jack</employee>
+ <employee id="2">Ann</employee>
+ </employees>
+</organization>
+
Example XPath | +Notes | +Result | +
---|---|---|
/organization |
+Finds all content within the organization element | +Jack Ann |
+
//employee[@id != 1] |
+// traverses all <employee> elements in document, the @id query selects all except those with id =1 |
+Jack | +
/organization/@name |
+@name to get the "name" property of the element |
+ExampleCo | +
/organization/employees/employee[2] |
+[2] specifies 2nd element |
+Ann | +
/organization/employees/employee[2]/@id |
+Get the "id" property of second employee element | +2 | +
/organization/employees/employee[@id=1] |
+Employee element with id property equal to "1" | +Jack | +
/organization/employees/employee[last()] |
+Last employee element | +Ann | +
//employee[contains(@id, "2")] |
+Employee within any parent element where id contains "2" | +Ann | +
For more examples, see W3CSchools or XPath Cheatsheet
+An action that allows replacing multiple inputs to a string with specified replacements. Additionally, Webhook.site will replace all variables in the source text as well as the text being replaced, and the replacement.
+Split text into multiple variables. Using hello,world
as Source, and ,
as Delimiter, 2 variables will be created: $variable.1$
is "hello" and $variable.2$
is "world".
Sets a variable depending on what maps to the source value and operator.
+If we set Source to John
, Operator to ends with
, Variable Name to $user_id$
, Default to unknown
, and add a mapping of From: John
-> To: 123
, then the variable name $user_id$
would be set to 123
.
If the Source had been Jack
, $user_id$
would have been set to unknown.
This will send a HTTP/HTTPS request from the Webhook.site cloud.
+The HTTP Request action has several modes:
+The response of the request is stored in a series of variable names prefixed with a value of your choosing. The following variables are set after the request has been fired:
+$your_prefix.content$
- response body content$your_prefix.status$
- response status code$your_prefix.headers$
- response headers$your_prefix.url$
- the URL the request was sent to$your_prefix.error$
- if the request resulted in an error, it's stored in this variable.This will send a email with variable contents from the Webhook.site cloud. Variables extracted previously can be used.
+This will send a email with variable contents from your own email provider. Variables extracted previously can be used.
+For Gmail, the following specific setup is required:
+smtp.gmail.com
587
TLS
Allows you to run one or more SSH command on a server. Webhook.site captures the output (stdout), stderr and the command exit code as Variables that can be used in downstream actions:
+$ssh.stdout$
$ssh.stderr$
$ssh.exit$
We recommend authenticating using a pre-generated keypair, which can be created under Control Panel -> Providers.
+Allows uploading a file to a SFTP (SSH) server, specifying a hostname, port, username, password, relative path to the file. The file content can be specified, in which Variables are replaced.
+We recommend authenticating using a pre-generated keypair, which can be created under Control Panel -> Providers.
+Allows uploading a file to a FTP or FTPS (FTP with TLS/SSL) server, specifying a hostname, port, username, password, relative path to the file, whether to use SSL and whether to use passive mode. Finally, the file content can be specified, in which Variables are replaced.
+We recommend storing the password as a Global Variable.
+Allows running a database query, with support for fetching out data in a series of variables. We recommend storing the password as a Global Variable.
+Currently supported are:
+If your database server is not on the list, please contact support.
+When using e.g. INSERT or UPDATE statements, we strongly recommend using parameters for each column value. Doing this, you avoid SQL injection attacks and other issues when using user-submitted data (e.g. via Variables), or even just data containing special characters like quotes, that could otherwise break a query.
+Each parameter name should start with a colon (:) and be a single word. You can then reference these parameters inside the query, like in the following example:
+ +When fetching data using e.g. SELECT statements, Webhook.site automatically inserts data in a series of Custom Action Variables, which are then available to downstream actions.
+For example, when fetching rows from the following table:
+ +Using the following statement:
+ +If the variable name prefix would be set to output
, the following variables would be created containing specific values:
Variable Name | +Value | +
---|---|
$output.0.id$ | +1 | +
$output.0.fname$ | +Simon | +
$output.0.lname$ | +Fredsted | +
$output.0.title$ | +Founder | +
$output.1.id$ | +2 | +
$output.1.fname$ | +Jack | +
$output.1.lname$ | +Daniels | +
$output.1.title$ | +Assistant | +
Additionally, a variable would be created with the name $output.json$
containing the data in JSON format:
[
+ {
+ "id": 1,
+ "fname": "Simon",
+ "lname": "Fredsted",
+ "title": "Founder"
+ },
+ {
+ "id": 2,
+ "fname": "Jack",
+ "lname": "Daniels",
+ "title": "Assistant"
+ }
+]
+
Marks the request so it is not saved in Webhook.site, which is useful (especially in combination with Conditions) when receiving a large amount of requests.
+Adds a custom log entry to the Request's action output.
+This action can be used to modify the response of the Webhook.site URL based on the input.
+This action can be used to allow a specific amount of requests in a specific amount of time per a given IP.
+If the IP is rate limited, the URL will respond with a HTTP 429
, action execution is stopped, and the request is not saved in Webhook.site.
Immediately stops Custom Action execution and returns the default response.
+Useful if you need to validate that the request does or does not conform to certain criteria, the Condition action will either stop or continue based on a condition.
+In both the input and the value fields, variables will be replaced (including Global Variables from the Control Panel), so you can compare e.g. JSONPath or Regex values - or even values from a previous HTTP request that was sent.
+Currently, three actions are provided: use result, stop and continue. Use Result allows using the Condition result in further actions. Stop will stop further action execution of the condition is a match. Continue will only continue further execution if the condition is a match, and otherwise stop.
+The following "operators" are available:
+The "result" of the condition will be logged below the request details, so you can see what happened.
+To use the result of the Condition, select it in the "only run when condition passes" checkbox:
+ +Tip: To check if a Variable is set (or exists), you must enter the variable name in both input and value fields and use the "is not equal to" operator, since non-existing variables are not replaced.
+With the JavaScript action, you can execute most kinds of JavaScript/Node.js code using a Node.js 20.10.0 sandbox.
+ +This feature is still in beta as of late 2023, and we expect to add more features in the coming months.
+Currently identified limitations include:
+console.log(line)
/ echo(line)
- log a string to Action output
set(variable_name, line)
- sets a Webhook.site variable for use in downstream actions
The following code would set the variable $myvar$ to value
:
get(variable_name, line)
- gets a Webhook.site variable (except Global Variables; see below)
variables[]
- global array variable containing Webhook.site variables
stop()
- stops action execution and return response
dont_save()
- marks current requests as Don't Save, so it won't be stored or shown in the Webhook.site requests list
respond(content, status, headers)
- stops action execution and return response
set_response(content, status, headers)
- sets response, but doesn't stop action execution
Code executed with the Webhook.site JavaScript action runs in a sandbox where the following utility libraries are available by using the require()
function:
axios
- HTTP Client
+ lodash
- General utility library
+ dayjs
- Date and time manipulation
+ cheerio
- JQuery-like HTML selector library
+ jsonpath
- JSONPath query library
+ crypto
– Node.js built-in crypto library
+ faker
- Seed data generator
+ Do you need a library that isn't listed here? Please contact support!
+For more information about WebhookScript, see the dedicated page.
+Defines (or overwrites) a variable that's available to downstream actions. The variable is not saved permanently as a Global Variable.
+There are three modes:
+Saves (or overwrites) a Global Variable that's saved permanently and available to all URLs in your account. If you don't need to save the variable permanently, you should use the Set Runtime Variable instead.
+Takes an image from either a URL or raw image data from e.g. a file upload, email attachment, request response or another action such as Dropbox.
+You can enter both width and height to contrain the image in both dimensions, or enter a single dimension.
+Check "Keep Aspect Ratio" so that the image keeps the aspect ratio, but doesn't exceed the height and width constraints.
+Note
+Google Sheets should not be used as a database, and have low usage limits. If you need to import on the order of thousands +of rows or make thousands of calls a day, Google Sheets cannot be used. We recommend using a database like Postgres in conjunction with the Database Query action.
+Google Sheets Custom Actions lets you manipulate and retrieve values from a Google Sheet.
+The following Google Sheets Custom Actions are available:
+To start, you need to make sure that you have connected a Google account in the Control Panel, available here.
+After that, you can select the account in the dropdown when creating the Custom Action.
+It is important to note that Google will block Write requests (i.e. adding or updating rows) at 60 requests per minute. After that, the action will temporarily fail with the following error message:
+Quota exceeded for quota metric 'Write requests' and limit 'Write requests per minute per user' of service 'sheets.googleapis.com' for consumer
+
Therefore, for importing mass amounts of data in a short timespan, Google Sheets is not recommended. Instead, we recommend using the Database Query action.
+Additionally, Webhook.site will automatically disable Google Sheets actions that continously fail due to e.g. quota errors.
+When specifying the spreadsheet, you can either just copy/paste the spreadsheet URL or enter the spreadsheet ID. Variables can be used to specify the spreadsheet.
+All actions must specify a range, which behaves similar in all actions. For the Add Row action, Google Sheets will automatically find a "table" (e.g. a homogenous mass of data) and add the values at the bottom.
+A range is the same query as in Google Sheets, e.g. to select A1-C3 in Worksheet "Example", enter 'Example'!A1:C3
.
When inserting or updating values, you can either enter a value in the text field, or supply multiple cells and/or rows using JSON. To insert two rows, the JSON would be ["cell 1", "cell 2"]
.
The Get Values Action allows you to define variables based on the output. Since this action can return multiple pieces of data, multiple variables are created.
+For example, if you select two columns and two rows, e.g. A1:B2
, four variables would be defined:
variable_name.0.0
= value of A1variable_name.0.1
= value of A2variable_name.1.0
= value of B1variable_name.1.1
= value of B2Additionally, the data is available in JSON, with the variable_name.json
variable being defined, and continuing with the example above, would contain the following JSON:
With Webhook.site, you can now use your Microsoft account to upload and download files in your OneDrive account using the Upload and Download Custom Actions.
+Using the Add Rows and Get Values actions, Webhook.site now allows using your Microsoft account to append and retrieve data from Excel worksheets in your OneDrive account.
+The following actions are available for AWS S3:
+In addition to the "official" Amazon endpoints, Webhook.site also supports S3-compatible storages like DigitalOcean, MinIO, Wasabi and more. The endpoint can be specified when setting up the account in Control Panel.
+The "Create Invalidation" action allows you to dynamically create a CloudFront cache invalidation as a Custom Action. Both the Distribution ID and the paths to be invalidated are replaced with Webhook.site Variables.
+With the Discord Custom Action, you can send messages to a specified channel (Each bot account uses a specific channel, so you can connect more accounts to send to different channels or servers.) In addition, you can choose a custom username and avatar image for the bot user.
+ +With the Slack Custom Action, you can easily use Slack's Webhook URLs to send messages to a channel.
+The Dropbox integration has access to the entire contents of your dropbox, and currently the following actions are available:
+The X Integration supports the following actions using X's API:
+The RabbitMQ Integration allows you to publish and consume messages from a RabbitMQ queue by specifying the server connection details.
+With the Send Push Notification action, you can easily send push notifications to your mobile devices using your Pushed.co account.
+ +With a free Pushed.co account, you can send up to 1000 push notifications a month.
+Allows you to easily send push notifications to your browser, phone, watch, etc. Simply download the ntfy.sh app, subscribe to your topic name and send a message to the topic name via this Custom Action. No account required.
+App download links:
+While Webhook.site doesn't have a native integration with Airtable, due to the simple API, it's exceedingly easy to add rows to a so-called Airtable Base.
+First, create an API key on your Account page: https://airtable.com/account.
+Next, go to Airtable's API documentation and select the Base you want to interact with: https://airtable.com/api. In the upper right corner, make sure show API key is checked.
+ +If you then scroll down to the Create records section, you can essentially copy everything over to a Send Request action.
+Things to note:
+POST
--data '
and the final '
(quote) characterVariables are the most important part of Custom Actions, and are characterized by a name surrounded by two dollar signs: $example$
. Variables can be used in any field that has a ⓥ icon in the editor. They act as placeholders that are replaced by dynamic content as the request or email is received.
Each request or email has a set of Base Variables (see below) that contain information like the request IP, method, headers, query string values, form values and the request content. To see a list of variables, click the Variables button in the editor. Clicking on a variable copies it to the clipboard.
+ +Many of the the available Custom Actions can register a variable during the runtime of the actions, so for example you can register the result of a JSONPath query and use it in a "Modify Response" action to make the response dynamic, or even use it to send a request to another HTTP address, and then use the response of that. Files can referred to and be used through Variables.
+This works since Custom Actions are executed synchronously in a chain, sharing data as they're being executed.
+The format of variables are dollar signs surrounded by a word, for example: $example$
.
In Webhook.site Control Panel, you can define Global Variables which can be accessed between all URLs and used in Schedules. Global Variables are permanent.
+Global Variables can also be created, modified or deleted using Custom Actions, including in WebhookScript and JavaScript actions.
+Additionally, Global Variables can be managed using the Webhook.site API.
+Adding specific suffixes to variable names will let you process the value in the following ways:
+Variable | +Example Input | +Example Output | +Description | +
---|---|---|---|
$example$ | +{"json": "value"} |
+{"json": "value"} |
+no modifier | +
$example.json$ | +{"json": "value"} |
+{\"json\": \"value\"} |
+Escapes all special JSON characters, allowing to use any string in a JSON object. Escaped characters include \b, \f, \n, \r, \t, ", \ | +
$example.html_encode$ | +<p>some html</p> |
+<p>some html</p> |
+Escapes all special HTML characters | +
$example.html_decode$ | +<p>some html</p> |
+<p>some html</p> |
+Replaces all escaped HTML escapes with normal characters | +
$example.html_strip$ | +<p>some html</p> |
+some html |
+Removes all HTML tags from input string | +
$example.base64_encode$ | +{"json": "<b>value</b>"} |
+eyJqc29uIjogIjxiPnZhbHVlPC9iPiJ9Cg== |
+Encodes the variable to base64 | +
$example.base64_decode$ | +eyJqc29uIjogIjxiPnZhbHVlPC9iPiJ9Cg== |
+{"json": "<b>value</b>"} |
+Decodes a base64 encoded string | +
$example.url_encode$ | +{"json": "value"} |
+%7B%22json%22%3A+%22value%22%7D |
+Escapes all special HTTP URL characters | +
$example.url_decode$ | +%7B%22json%22%3A+%22value%22%7D |
+{"json": "value"} |
+Replaces all special HTTP URL escapes with normal characters | +
These variables are automatically available for each request or email. Different variables are available depending on the type.
+Variable Name | +Available For | +Description | +
---|---|---|
request.uuid | +All | +The UUID of the request | +
request.token_id | +All | +The Token UUID (URL ID) of the request | +
request.content | +All | +The body content of the request | +
request.date | +All | +Creation date in Y-m-d H:m:s format | +
request.date | +All | +Creation date in UNIX timestamp format | +
request.hostname | +All | +Hostname of the request (usually webhook.site ) |
+
request.header.[name] | +All | +Created for each HTTP header | +
request.size | +All | +Request body size in bytes | +
request.type | +All | +Request type (email or web ) |
+
request.file.[name].filename | +All | +Created for each file upload, with name being the input name property. Contains the client file name |
+
request.file.[name].size | +All | +Contains the file size in bytes | +
request.file.[name].content | +All | +Contains the file content | +
request.file.[name].content_type | +All | +Contains the file content type (e.g. image/png) | +
request.file.[name].id | +All | +Contains the Webhook.site file ID | +
request.file.[name].link | +All | +Contains the direct download link to the file from Webhook.site's server. | +
request.query.[name] | +Web | +Created for each query string (e.g. ?name=value) | +
request.form.[name] | +Web | +Created for each form field | +
request.ip | +Web | +IP of the host making the request | +
request.user_agent | +Web | +User agent header | +
request.url | +Web | +Full URL of the request (e.g. https://webhook.site/xxx-xxx...) | +
request.path | +Web | +Sub-path of URL, after the token ID. Defaults to / |
+
request.query | +Web | +Full query string of the URL | +
request.method | +Web | +HTTP method (GET, POST, etc.) | +
request.sender | +Sender address | +|
request.message_id | +Email message ID | +|
request.text_content | +Parsed plaintext content | +|
request.destinations | +Comma separated list of recipients. | +|
request.checks.[name] | +True or false for email checks (DKIM, SPF, etc.) | +
Webhook.site DNSHook is a type of hook, like webhooks and emailhooks, that on Webhook.site will automatically show all DNS requests to a unique DNS name and all its subdomains.
+DNSHooks can be used to send data solely via DNS. DNSHooks can also be used as a canary token as DNS can be useful in cases where it can bypass firewalls, for example.
+ +You can even create workflows from DNSHook requests with Custom Actions.
+For example, you can send a notification to your phone whenever a specific subdomain has had its DNS looked up via the Ntfy action.
+As all subdomains are also logged and shown on Webhook.site, you can send data in DNSHooks by e.g. base64-encoding a subdomain. For example, the string hello world
can be encoded in a DNS lookup as the following.
(In this case, aGVsbG8gd29ybGQ
base64-decodes to hello world
and 47bb8761-e40b-42c2-8c98-a448f492df70
is the ID of the Webhook.site token/URL.)
Indeed, you can even use different record types, like CNAME and MX, as flags to transfer information to Webhook.site.
+You can also set the DNS response dynamically by using the URL response in the following format. You can return one or more responses.
+[
+ {
+ "type": "a",
+ "value": "127.0.0.1"
+ },
+ {
+ "type": "cname",
+ "value": "example.com."
+ },
+ {
+ "type": "txt",
+ "value": "hello world"
+ }
+]
+
To set the response, either enter the JSON in Edit → Content, or use the Modify Response Custom Action and setting the Response Body field.
+ +type
can be one of the following:
a
cname
txt
Note: If the response isn't valid (e.g. invalid JSON, using a domain name in an A record), no response is returned. It is currently not possible to be notified of DNS parsing errors, so make sure to test this well.
+For txt
responses, multiple lines can be sent by separating with \n
(newline).
The term 'webhook' refers to the general technology of Web-based systems communication.
+In short, many systems (e.g. a payment platform and a customer management system) communicate with each other by sending Web requests back and forth, e.g. from https://paymentsys.example to https://customersys.example/register-payment.
+We don't offer support or help with general questions or issues with webhooks. More about webhooks.
+Webhook.site is a tool for building software that use webhooks, either by allowing developers to inspect the data that's being sent via a webhook, but also helps users create workflows that respond to and interact with webhooks from various systems.
+With Webhook.site, users instantly get a unique, random URL and e-mail address. Everything that's sent to these addresses are shown instantly. With this, users can test and debug Webhooks and HTTP requests, as well as create workflows using the Custom Actions graphical builder or WebhookScript, a simple scripting language, to transform, validate and process HTTP requests in a variety of ways – without setting up and maintaining your own infrastructure.
+Webhook.site company stats as of March 2024:
+Yes. Per default, all URLs associated with a paid Webhook.site subscription are protected with login. Additionally, users can decide exactly how much (or little) data Webhook.site stores, either by amount of requests/emails or
+For free users, data is accessible to anyone who knows the ID of the URL.
+Yes. Thousands of our customers use Webhook.site to build workflows that help their business, without needing to hire a programmer or pay for and setup servers. We take care of the infrastructure so you can build what you need.
+For each URL associated with a Webhook.site Pro account, Webhook.site makes the latest 10.000 requests or emails available. Old requests are automatically rotated/purged periodically.
+For free users, the amount is 100 and old requests are not automatically rotated. Instead, the URL stops accepting new requests after the limit has been met.
+For Free users, the URL – and its data – is automatically removed after 7 days with no activity.
+For Pro and Enterprise URLs, URLs never automatically expire, but data is removed after 365 days.
+To hand over your account to a different business or individual you essentially only need to change the email address, as it is the only piece of identification we store. However, be aware of the following:
+If billing details need to change, make sure to cancel the subscription first. The subscription will continue to work until the expiry date.
+If 2-Factor Authentication is enabled, disable 2-Factor.
+For Enterprise users, make sure you delete any extraneous users.
+Delete any extraneous API keys that you should no longer have access to.
+Change the account email address to the receiver's email address.
+You, or the account receiver, can then issue a Password Reset email so they can set their own password.
+The account receiver can then create a new subscription on the account.
+After this, you will no longer have access to your account and it is fully handed over.
+You'll need to whitelist the IPs 46.4.105.116
and 88.99.82.58
.
Both inbound and outbound originate and destinate at this IP address.
+Note that this may change in the future, so sign up for the newsletter to be notified of changes.
+You can use the Conditions Custom Action to add a quick header based authentication mechanism to your URL. You can also add the Don't Save action as a condition if you don't wish to save the unauthenticated request.
+ +We also have an example WebhookScript for adding basic auth:
+username = 'john'
+password = '1234'
+
+if (var('request.header.php-auth-user', '') != username and var('request.header.php-auth-pw', '') != password) {
+ respond('', 401, ['WWW-Authenticate: Basic realm="Authentication required"']);
+ dont_save()
+}
+
+respond('Login OK!');
+
Via Custom Actions, Webhook.site supports the service Pushed, which has a free tier. More info here.
+Alternatively, at least on iPhones, you can use the Send Email custom action and mark the sender address as VIP. This will trigger a push notification when the email is received.
+After you've paid for and created a subscription, you will receive an invoice via email.
+If you are an enterprise customer that wishes to buy a subscription via bank transfer, please contact Support.
+Go to Control Panel -> Billing, then click View next to an invoice and finally click the "Add Address & VAT Number" link.
+Once you start the checkout process, you will see the amount in your local currency, including any applicable VAT.
+The JSON data might have been attached to the request as form data rather than as request body data, which is usually how JSON is sent.
+The data might look like this on Webhook.site:
+ +To remediate this in Extract JSONPath, you'll need to set the source field to the form field variable, which is automatically set by Webhook.site. In the screenshot above, the variable name would be $request.form.my_json_data$
, which works with Extract JSONPath:
If you use any variables in the JSON that could contain e.g. new lines or quote characters, these characters need to be "escaped" properly so that the JSON remains valid.
+Webhook.site provides an easy way to do this with the .json
Variable Modifier, which will automatically escape any special JSON characters. More info here.
Before:
+ +After, with the JSON Escape Variable Modifier:
+ +With Webhook.site Pro, there's a variety of different ways to export data sent to your URL or email address.
+Custom Actions can be used in a variety of ways to export data. Below are listed a few examples of actions that could be used. More info here.
+ +Webhook.site provides a CSV Export functionality, simply click the button in the menu to download all data as a CSV file.
+ +Data can be retrieved and saved using the Webhook.site API using any programming language.
+With the Webhook.site CLI (Command-Line Interface), requests can be forwarded directly from Webhook.site to a local workstation or server. More info here
+There's several ways to accomplish this depending on your needs.
+You can periodically fetch the data using the Webhook.site API
+Requests can also be streamed to a local URL using the Webhook.site CLI, in a similar fashion to e.g. ngrok.
+Webhook.site also supports the XHR Redirect feature, which uses your browser in order to forward the requests. The endpoint will need to respond with CORS headers in all requests so that the browser will be able to send requests to it. The forwarding will only work as long as the browser window is open.
+The following CORS headers should allow Webhook.site to forward requests to your local endpoint via XHR Redirect:
+Access-Control-Allow-Origin: *
+Access-Control-Allow-Methods: *
+Access-Control-Allow-Headers: *
+Access-Control-Expose-Headers: Content-Length,Content-Range
+
If you access Webhook.site with the hostname https://ipv6.webhook.site, your URL will only work with IPv6.
+To use it with a URL, simply replace the webhook.site
hostname with ipv6.webhook.site
.
Using the free version of Webhook.site, URLs automatically expire in 7 days. After that, the URL is no longer available and data is deleted.
+With the paid version, Webhook.site Pro, URLs never expire automatically.
+You might be copying the URL for the Webhook.site application (by copying the link from your browsers' address bar), and not the actual URL.
+Webhook.site app (⛔️ wrong):https://webhook.site/#!/view/6dbb3859-4ad5-4e85-acae-e44d6e37ea4a
Webhook.site url (✅ correct):https://webhook.site/6dbb3859-4ad5-4e85-acae-e44d6e37ea4a
First, make sure that you have copied the correct URL, see here.
+For free users: The URL has met the limit of the amount of requests it can receive. To unlock more requests, the URL must be associated with a Webhook.site Pro or Enterprise account. To associate a URL to your account, click Upgrade in the upper-right corner when logged in.
+For Webhook.site Pro or Enterprise users: If you're getting this error with a Webhook.site URL, the URL may have been automatically blocked due to an extraordinarly large amount of requests, as per the Fair Use guidelines in our Terms of Service. This is done to prevent a decrease in service level for our other customers. For more info, or to request a whitelisting, please contact Support. If you're getting this error with a Webhook.site API endpoint, you have exceeded the API endpoint quota, and should implement e.g. a backoff strategy.
+The HTTP body data (e.g. files or JSON data) submitted to Webhook.site must be below 10 megabytes. More than that will cause a HTTP 413 response.
+If you're requesting the Webhook.site endpoint from another domain via JavaScript, you'll need to enable CORS so the browser allows the request.
+To do this, click Edit in the upper-right corner, check the Add CORS headers checkbox, and click Save.
+Our SSL certificate is fully working; the issue lies with your system. In september 2021, our SSL provider, LetsEncrypt, updated their root certificate. This can mean that if your locally installed trusted root certificates are of an old version, you'll be seeing a certificate error as Webhook.site now runs a certificate that is based on the new root certificate chain.
+To remediate this problem, you'll need to update your local certificate trust store:
+update-ca-certificates
; more info here.yum update ca-certificates
; more info hereSome systems and packages, like Python certifi/urllib3, also come with static certificates. We cannot support updating these.
+For more information about DNSHook, please see here.
+ +With Webhook.site, you instantly get a unique, random URL and e-mail address. Everything that's sent to these addresses are shown instantly. With this, you can test and debug Webhooks and HTTP requests, as well as create your own workflows using the Custom Actions graphical editor or WebhookScript, a simple scripting language, to transform, validate and process HTTP requests in a variety of ways – without setting up and maintaining your own infrastructure.
+What are customers using Webhook.site for?
+The following video is a quick demo of Webhook.site Custom Actions is used to set up a workflow to post updates from a news page to an X/Twitter account automatically.
+Webhook.site is operated by Webhook ApS (VAT ID: DK41561718).
+Address: Damhusvej 95, 3 tv, 5000 Odense, Denmark.
+Founded and built by Simon Fredsted (@fredsted).
+ +Webhook.site is a small startup that's fully remote. We believe in absolute trust in our employees. What's important to us is your work output and communication, not how many hours you spend. You'll be working at a company with a lot of dedicated and passionate customers that use the product to solve real problems in their work.
+Customers use Webhook.site to solve a wide variety of problems, and it's your job to help them use Webhook.site to complete their goals.
+Users can be at the beginner to intermediate stage in terms of their understanding of Internet technology, scripting etc., and often you’ll need to sense what the actual problem is–which can be wildly different from what the support ticket is about.
+100% remote. Around 10 hours per week.
+To apply, send an email to jobs@webhook.site. We expect you to send us a CV along with a short email describing yourself, and then we'll do a half-hour video call to talk about the job and any questions you may have. If we decide to hire you, you'll be able to start as soon as you like.
+Webhook.site is a complex Laravel and Angular.js based system with more than 100.000 monthly unique users which use it for everything from Webhook API development to workflow automation using Webhook.site’s Custom Actions feature. Much of the site is a REST API with an Angular.js client in addition to some Blade pages.
+Your job is to continuously improve on the codebase, whether it’s code organisation, framework and dependency updates, implementing new features and fixing bug tickets.
+The job is paid hourly, and you’ll be 100% free to schedule your work as you see fit and work as may or as few hours as you like.
+To apply, send an email to jobs@webhook.site. We expect you to send us a CV along with a short email describing yourself, and then we'll do a half-hour video call to talk about the job and any questions you may have. If we decide to hire you, you'll be able to start as soon as you like.
+ +Subscribe below to receive updates about improvements and new features on Webhook.site as well as infrastructure changes like new IP addresses for e.g. firewall whitelisting. Expect a newsletter a few times a year at most.
+ + +$request.query$
Base Variable not being generated.gzip
encoded request bodies.faker
library added to the JavaScript Custom Action. More info here\n
as a newline, allowing splitting strings by line.$request.path$
(contains any subpath after the Webhook.site URL ID) and $request.query$
(contains the query string) variables added. More info heredate_from
, date_to
, and query
parameters. More info here.Forward
mode.http()
, which replaces the request()
function and allows for more flexibility as well as sending e.g. form/multipart requests as the new HTTP Request Custom Action also allows. More info hereimage/*
, video/*
and application/pdf
are now shown as linked files in the file list, not binary data.ipv6.webhook.site
hostname. More info heremultipart()
, for sending HTTP multipart requests. More info here.xml2array()
logs more helpful errors.xml2array()
. More info here.string_random()
, uuid()
. More info here.sign()
, digest()
, verify()
. More info here. JWT example here.base64url_encode()
, base64url_decode()
. More info here.array_splice
and array_range
functions. More info herexpath
functions would not match correctly in all cases.is_empty()
More info hereis_null()
- handy for checking if a value is null without type checking issues. More info here429 Too Many Requests
response.false
by default when this third parameter is not set. More info here$request.ip$
) is still the default key.$request.file.[name].id$
and $request.file.[name].link$
(which returns direct link to download the file.)num2hex
, hex2num
and hex2bin
functions.group_id
parameter when creating or updating Tokens./token/:tokenId/requests
are now throttled at 60 requests/minute.Webhook.site/1.0
, which can be overwrited if specifying a User-Agent header manually.Invalid namespace
errors..html_decode
, .url_encode
, and .url_decode
. More info hereclone_from
option when using the API to create Tokens (URLs)json_decode
/ json_encode
now output error messages if they fail due to e.g. bad data.array_sort
, array_join
and json_escape
functions.array_keys
and array_values
functions. For more information, see here.timeout
parameter can also be given to the request()
function in WebhookScript. Both places accept a value in seconds, with decimals.override
parameter to request(), which prevents content from the source request from being included.var()
function..data[?(@.Employee.FirstName)]
.to_date
and date_format
functions have been improved with timezone handling capabilities.trim(string)
, which removes space, newline and tab characters from the beginning and end of a string, similar to PHP's own trim() function.action(action_type, parameters)
, to run Custom Actions inside a WebhookScript. More info here.files()
retrieves a an array of all files, file_content(fileId)
returns the content of a specific file. More info here.request.file.<formname>.content
, request.file.<formname>.size
, request.file.<formname>.filename
csv_to_array()
, which converts a CSV file from a string to an array that can easily be parsed.to_date()
, date_format()
, date_to_array()
, date_interval()
, date_interval_human()
. Read more in the docsdelay()
, which lets you delay and execute WebhookScript code a set amount of seconds in the future.exec()
, which lets you dynamically execute code in a string.import()
, which downloads and executes code from a URL - great if you want to reuse your code, just put it on Github and import it with the URL!base64_encode()
, base64_decode()
.regex_extract
and regex_extract_first
functions.hash
function.url_encode
, url_decode
, and query
functions.\\n
) in strings, escaped by 2 backslashes.Global Variables are now available in the Control Panel, which lets you keep configuration like API keys in a separate place from your Custom Actions and scripts while managing them at a central place.
+Changed the font of the WebhookScript editor, which resulted in uneven selection of text.
+There are two separate editions of Webhook.site:
+The code for the completely open-source, MIT-licensed version described on this page is available on https://github.com/webhooksite/webhook.site, and can be self-hosted using e.g. Docker, is great for testing Webhooks, but doesn't include Webhook.site Pro features like Custom Actions.
+The cloud version of Webhook.site at https://webhook.site which has more features, some of them requiring a paid subscription.
+You can choose to run the Open Source version of Webhook.site either via Docker, or install it on any Web server with PHP7 support.
+laravel-echo-server or Pusher can be used to enable realtime updates. Take a look at the .env.example
to see the environment variables required to configure it.
For laravel-echo-server
, the app expects socket.io to be available at the /socket.io
path relative to the index page. This can be done with nginx like so:
location /socket.io {
+ proxy_pass http://127.0.0.1:6001;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_set_header Host $host;
+ proxy_cache_bypass $http_upgrade;
+ }
+
The provided Docker Compose file sets up a complete environment that runs the Webhook.site image and all dependencies (Redis, Laravel Echo Server, etc.). Note that if running this in production, you should probably run a Redis server that persists data to disk. The Docker image is also not tuned for large amounts of traffic.
+docker-compose up
A set of Kubernetes configuration files can be found in the kubernetes
subfolder.
Configure the resources, and apply with kubectl apply -f ./
.
DigitalOcean has a guide on how to configure nginx.
+composer install
cp .env.example .env
- adjust settings as neededphp artisan key:generate
/public
folder. With Webhook.site Basic, Pro and Webhook.site Enterprise subscriptions, you get more features that allows you to do more with your URL, as well as keep the data contained private and secure.
+ ++ | Free | +Basic | +Pro | +Enterprise | +
---|---|---|---|---|
URLs | +n/a | +1 | +Unlimited | +Unlimited | +
Schedules | +n/a | +1 | +Unlimited | +Unlimited | +
Custom Actions | +❌ | +✅ | +✅ | +✅ | +
Schedules | +❌ | +✅ | +✅ | +✅ | +
Custom Addresses | +❌ | +✅ | +✅ | +✅ | +
Secure Data | +❌ | +✅ | +✅ | +✅ | +
Permanent URLs | +❌ | +✅ | +✅ | +✅ | +
CSV Export | +❌ | +✅ | +✅ | +✅ | +
Free Email Support | +❌ | +✅ | +✅ | +✅ | +
Custom Domain | +❌ | +❌ | +❌ | +✅ | +
Multi-User Support | +❌ | +❌ | +❌ | +✅ | +
Request History | +100 (hard limit) | +10.0002 | +10.0002 | +10.0002 | +
URL/Email Address expiry | +7 days1 | +Never expires | +Never expires | +Never expires | +
Data storage duration | +7 days1 | +365 days | +365 days | +365 days | +
Pricing3 | ++ | $9/month $90/year (save 20%) | +$18/month $180/year (save 20%) | +$49/month $499/year (save 20%) | +
Extract data, send emails and HTTP requests, upload files, run scripts and much more with Custom Actions that are run on each incoming request or email, with the Webhook.site Custom Actions builder.
+Native integrations for AWS, Google Sheets, Slack, Dropbox, MySQL and others.
+ +Use Schedules by Webhook.site to send requests any URL or run your Custom Actions automatically on an interval. Use it for monitoring your Web site, clearing caches, and much more. Supports cron
syntax, in addition to predefined intervals.
Get an address that's easy to remember:
+webhook.site/my-alias
my-alias@email.webhook.site
(With Webhook.site Enterprise, you can even use your own domain name.)
+Your data is secured in your account, and can be accessed by API Key or login. With the free version of Webhook.site, others can freely view the requests sent to your URL.
+The URLs stay in your account forever, the 7-day auto expiration of the free version is removed.
+With the free version, URLs stop taking in new requests or emails after the 500 request limit is reached. With Pro, the limit of max 500 requests or emails per URL is removed and raised to 10.000 requests or emails, which are then rotated automatically.
+Create as many upgraded URLs or email addresses as you need at no extra cost.
+Export all requests and emails to a CSV file.
+Forward requests to your Webhook.site URL to your local network via Webhook.site CLI.
+We’ll help you troubleshoot and set up your Custom Actions and workflows.
+In addition to all Pro features, Enterprise also contains the following features:
+Use a your own (sub)domain for URLs and emails:
+mydomain.com/my-alias
my-alias@email.mydomain.com
Additionally, Webhook.site provides an SSL certificate which is included free of charge with your Enterprise subscription.
+Ideal for larger organizations, share URLs, schedules, and other data with the rest of the organization. Configure individual users' access with User Types like Administrator and Viewer, so some users may only view the content.
+ +Included in your Webhook.site Pro subscription is Webhook.site Schedules, which enables you to periodically send requests to specified URLs (also including your Webhook.site URLs, so your Custom Actions can be executed periodially) with a custom method, headers, and interval.
+Schedules can be used for a variety of purposes, including cache warming, uptime monitoring, automatic data transfer, etc.
+After creating the Schedule, you can view the logs for the last 100 scheduled requests.
+Per default, the timeout for the Schedule requests is 5 seconds, but can range from 1 to 30 seconds.
+A timeout or server error will trigger a Schedule error notification email, if enabled in Control Panel -> Settings.
+Schedules can also be managed using the Schedules API.
+ +If you create a Schedule and enable the When an error happens during a Schedule run setting, Webhook.site will automatically send an email notification when a URL has a timeout longer than the one specified, or when one of the requirements aren't met. It is possible to specify requirements for the response body containing a string, and the response status code being within a certain range.
+In addition to be able to use a custom cron
-style expression string, Schedules can be executed at the following preset intervals:
Schedule intervals are based on UTC time.
+For building cron-style expressions, we recommend https://crontab.guru.
+ +With Webhook.site, you instantly get a unique, random URL and e-mail address. Everything that's sent to these addresses are shown instantly. With this, you can test and debug Webhooks and HTTP requests, as well as create your own workflows using the Custom Actions graphical editor or WebhookScript, a simple scripting language, to transform, validate and process HTTP requests in a variety of ways \u2013 without setting up and maintaining your own infrastructure.
What are customers using Webhook.site for?
The following video is a quick demo of Webhook.site Custom Actions is used to set up a workflow to post updates from a news page to an X/Twitter account automatically.
"},{"location":"index.html#company-information","title":"Company information","text":"Webhook.site is operated by Webhook ApS (VAT ID: DK41561718).
Address: Damhusvej 95, 3 tv, 5000 Odense, Denmark.
Founded and built by Simon Fredsted (@fredsted).
Contact information.
"},{"location":"index.html#acknowledgements","title":"Acknowledgements","text":"The Webhook.site Command Line Interface listens for requests to a Webhook.site URL and forwards/redirects them to your local computer or server.
Star
"},{"location":"cli.html#installation","title":"Installation","text":""},{"location":"cli.html#docker","title":"Docker","text":"After installing Docker, you can run Webhook.site CLI via docker run
:
docker run -ti webhooksite/cli -- whcli help
Node version 14 or greater required.
To install: npm install -g @webhooksite/cli
Then you can run e.g. whcli help
Some command arguments can be specified via environment variables:
--token WH_TOKEN\n --api-key WH_API_KEY\n --target WH_TARGET\n --command WH_COMMAND\n
General environment variables:
WH_LOG_LEVEL
Sets log level (silent, trace, debug, info, warn, error, fatal.) Defaults to info.
For some commands and arguments, runtime variables can be replaced with the standard Webhook.site Custom Actions syntax (e.g. $variable$). Global Variables are not replaced.
The variables that are available are the default base variables and any variables defined in a Custom Action that was run during the request.
"},{"location":"cli.html#commands","title":"Commands","text":""},{"location":"cli.html#help-list-commands","title":"help
: List commands","text":"Lists commands available to the CLI.
"},{"location":"cli.html#forward-forward-requests","title":"forward
: Forward requests","text":"The forward
command listens for new incoming requests sent to your Webhook.site URL and immediately relays them to any URL you specify, or simply localhost
(so it can be used as an ngrok alternative). This URL can be any URL that the machine running Webhook.site CLI can access.
--token
) parameter must specify the Webhook.site URL ID (also called token ID). The token ID is the long 36-character ID at the end of your Webhook.site URL.--api-key
) must also be specified when the token belongs to a Webhook.site account, and can be generated from the Webhook.site Control Panel.--target
) specifies where traffic should be redirected (defaults to https://localhost
)whcli forward \\\n--token=1e25c1cb-e4d4-4399-a267-cd2cf1a6c864 \\\n--api-key=ef6ef2f8-3e48-4f77-a54c-3891dc11c05c \\ \n--target=https://example.com\n
Custom Action variables are replaced in the --target
argument.
The request method, headers and any additional path or query string parameters added to the Webhook.site URL is forwarded on to the target. For example, if the target URL is https://example.com
, sending a POST request to https://webhook.site/c33f3c3e-6018-4634-b406-65338edee460/example?query=value
, the target URL will also receive a POST request on https://example.com/example?query=value
.
When running Webhook.site CLI in Docker, to access the host machine, you can't use localhost
or 127.0.0.1. Instead, use the special hostname host.docker.internal
. More info here.
If the target uses a self-signed certificate, you could get an error message about this. To allow self-signed certs, run whcli
with the following environment variable and value:
NODE_TLS_REJECT_UNAUTHORIZED=\"0\"\n
"},{"location":"cli.html#exec-execute-commands","title":"exec
: Execute commands","text":"Allows executing terminal commands for new incoming requests sent to your Webhook.site URL. Custom Action variables are replaced in the --command
argument.
whcli exec \\\n--token=1e25c1cb-e4d4-4399-a267-cd2cf1a6c864 \\\n--api-key=ef6ef2f8-3e48-4f77-a54c-3891dc11c05c \\ \n--command='ping $request.ip$'\n
"},{"location":"custom-actions.html","title":"Custom Actions","text":"With Custom Actions, it is possible to create a workflow out of a set of actions that are executed whenever a Webhook.site URL receives a request or email.
Using this functionality, you can connect APIs that aren't compatible, convert a HTTP request to an email or vice versa, build workflows that would otherwise require a developer, and much, much more.
"},{"location":"custom-actions.html#demos","title":"Demos","text":""},{"location":"custom-actions.html#javascript-x-and-schedules","title":"JavaScript, X and Schedules","text":"In this video I'm doing a quick demo of how I used Webhook.site to set up a workflow to post updates from my news page automatically to my X/Twitter account. I used the actions JavaScript, Conditions, X Post, and Store Global Variable.
"},{"location":"custom-actions.html#jsonpath-and-google-sheets","title":"JSONPath and Google Sheets","text":"In the following demo, webshop order details are received in a webhook. We then use Extract JSONPath and Google Sheets actions to insert their name in a Google Sheet. It also shows how variables interact with downstream actions.
"},{"location":"custom-actions.html#templates","title":"Templates","text":"If you use the same set of Custom Actions often, you can create a Template that contains a copy of one or more Custom Actions, along with a set of Predefined Variables.
You can then use this template by creating an Import Template action.
"},{"location":"custom-actions.html#creating-a-template","title":"Creating a Template","text":"To create a Template, click the Create Template button at the bottom of the Custom Actions overlay. Then enter a name for the template and select which actions should be copied to the template. If your template actions e.g. depend on variables pre-existing, like configuration data, you can add one or more Predefined Variables. These are available to the actions in the Template, but also to any subsequent actions coming after the Import Template action.
Predefined Variables can be updated in the Control Panel.
Changes to the actions that the Template was created from do not automatically carry over to the template. Instead, Templates can be updated post-creation by overwriting them with a new set of actions.
Templates can also be managed using the Template API.
"},{"location":"custom-actions.html#repeating-actions","title":"Repeating Actions","text":"Webhook.site allows some action types to be repeating, which makes Webhook.site \"loop over\" one or more values without needing to use scripts.
Currently, repetition is only supported by the Extract JSONPath and Extract Regex action types.
Currently, the maximum amount of items that are supported is 100 to prevent abuse. This limit may be raised in the future. Items above that are ignored.
The repeating action should be ordered before the actions that are to be repeated. The actions that are repeated will run for each item that is extracted, using the same variable name.
"},{"location":"custom-actions.html#queued-actions","title":"Queued Actions","text":"By checking the Queued checkbox when creating a Custom Action, Webhook.site will run that specific action in a background queue (asynchronously).
This is useful when you need your Webhook.site URL to respond quickly, but your Custom Actions are taking a long time to run. For example, if your Webhook.site URL should respond in 5 seconds, but you need to call an endpoint with a HTTP Request action that responds in 10 seconds, you can queue the HTTP Request action.
Additionally, you can specify an amount of seconds to wait until the action is executed. To do this, enter an amount of seconds in the Seconds textbox next to the Queue checkbox.
As the a queued action will inherit the execution scope up until the action, there are a few things to be aware of when using Queued Actions:
Webhook.site DNSHook is a type of hook, like webhooks and emailhooks, that on Webhook.site will automatically show all DNS requests to a unique DNS name and all its subdomains.
DNSHooks can be used to send data solely via DNS. DNSHooks can also be used as a canary token as DNS can be useful in cases where it can bypass firewalls, for example.
"},{"location":"dnshook.html#create-dns-workflows-with-custom-actions","title":"Create DNS workflows with Custom Actions","text":"You can even create workflows from DNSHook requests with Custom Actions.
For example, you can send a notification to your phone whenever a specific subdomain has had its DNS looked up via the Ntfy action.
"},{"location":"dnshook.html#receive-data-via-dns","title":"Receive data via DNS","text":"As all subdomains are also logged and shown on Webhook.site, you can send data in DNSHooks by e.g. base64-encoding a subdomain. For example, the string hello world
can be encoded in a DNS lookup as the following.
aGVsbG8gd29ybGQ.47bb8761-e40b-42c2-8c98-a448f492df70.dnshook.site\n
(In this case, aGVsbG8gd29ybGQ
base64-decodes to hello world
and 47bb8761-e40b-42c2-8c98-a448f492df70
is the ID of the Webhook.site token/URL.)
Indeed, you can even use different record types, like CNAME and MX, as flags to transfer information to Webhook.site.
"},{"location":"dnshook.html#send-dns-response","title":"Send DNS Response","text":"You can also set the DNS response dynamically by using the URL response in the following format. You can return one or more responses.
[\n{\n\"type\": \"a\",\n\"value\": \"127.0.0.1\"\n},\n{\n\"type\": \"cname\",\n\"value\": \"example.com.\"\n},\n{\n\"type\": \"txt\",\n\"value\": \"hello world\"\n}\n]\n
To set the response, either enter the JSON in Edit \u2192 Content, or use the Modify Response Custom Action and setting the Response Body field.
type
can be one of the following:
a
cname
txt
Note: If the response isn't valid (e.g. invalid JSON, using a domain name in an A record), no response is returned. It is currently not possible to be notified of DNS parsing errors, so make sure to test this well.
For txt
responses, multiple lines can be sent by separating with \\n
(newline).
The term 'webhook' refers to the general technology of Web-based systems communication.
In short, many systems (e.g. a payment platform and a customer management system) communicate with each other by sending Web requests back and forth, e.g. from https://paymentsys.example to https://customersys.example/register-payment.
We don't offer support or help with general questions or issues with webhooks. More about webhooks.
"},{"location":"faq.html#whats-webhooksite","title":"What's Webhook.site?","text":"Webhook.site is a tool for building software that use webhooks, either by allowing developers to inspect the data that's being sent via a webhook, but also helps users create workflows that respond to and interact with webhooks from various systems.
With Webhook.site, users instantly get a unique, random URL and e-mail address. Everything that's sent to these addresses are shown instantly. With this, users can test and debug Webhooks and HTTP requests, as well as create workflows using the Custom Actions graphical builder or WebhookScript, a simple scripting language, to transform, validate and process HTTP requests in a variety of ways \u2013 without setting up and maintaining your own infrastructure.
Webhook.site company stats as of March 2024:
Yes. Per default, all URLs associated with a paid Webhook.site subscription are protected with login. Additionally, users can decide exactly how much (or little) data Webhook.site stores, either by amount of requests/emails or
For free users, data is accessible to anyone who knows the ID of the URL.
"},{"location":"faq.html#can-i-use-webhooksite-for-production-workloads","title":"Can I use Webhook.site for production workloads?","text":"Yes. Thousands of our customers use Webhook.site to build workflows that help their business, without needing to hire a programmer or pay for and setup servers. We take care of the infrastructure so you can build what you need.
"},{"location":"faq.html#how-much-data-does-webhooksite-store","title":"How much data does Webhook.site store?","text":"For each URL associated with a Webhook.site Pro account, Webhook.site makes the latest 10.000 requests or emails available. Old requests are automatically rotated/purged periodically.
For free users, the amount is 100 and old requests are not automatically rotated. Instead, the URL stops accepting new requests after the limit has been met.
"},{"location":"faq.html#how-long-is-data-stored-on-webhooksite","title":"How long is data stored on Webhook.site?","text":"For Free users, the URL \u2013 and its data \u2013 is automatically removed after 7 days with no activity.
For Pro and Enterprise URLs, URLs never automatically expire, but data is removed after 365 days.
"},{"location":"faq.html#how-do-i-transfer-my-webhooksite-account-and-data","title":"How do I transfer my Webhook.site account and data?","text":"To hand over your account to a different business or individual you essentially only need to change the email address, as it is the only piece of identification we store. However, be aware of the following:
If billing details need to change, make sure to cancel the subscription first. The subscription will continue to work until the expiry date.
If 2-Factor Authentication is enabled, disable 2-Factor.
For Enterprise users, make sure you delete any extraneous users.
Delete any extraneous API keys that you should no longer have access to.
Change the account email address to the receiver's email address.
You, or the account receiver, can then issue a Password Reset email so they can set their own password.
The account receiver can then create a new subscription on the account.
After this, you will no longer have access to your account and it is fully handed over.
"},{"location":"faq.html#i-want-to-whitelist-webhooksite-in-our-firewall-which-ip-do-you-use","title":"I want to whitelist Webhook.site in our firewall, which IP do you use?","text":"You'll need to whitelist the IPs 46.4.105.116
and 88.99.82.58
.
Both inbound and outbound originate and destinate at this IP address.
Note that this may change in the future, so sign up for the newsletter to be notified of changes.
"},{"location":"faq.html#how-do-i-add-authentication-to-my-url","title":"How do I add authentication to my URL?","text":"You can use the Conditions Custom Action to add a quick header based authentication mechanism to your URL. You can also add the Don't Save action as a condition if you don't wish to save the unauthenticated request.
We also have an example WebhookScript for adding basic auth:
username = 'john'\npassword = '1234'\nif (var('request.header.php-auth-user', '') != username and var('request.header.php-auth-pw', '') != password) {\nrespond('', 401, ['WWW-Authenticate: Basic realm=\"Authentication required\"']);\ndont_save()\n}\nrespond('Login OK!');\n
"},{"location":"faq.html#can-i-get-a-push-notification-on-my-phone-when-my-url-receives-a-request","title":"Can I get a push notification on my phone when my URL receives a request?","text":"Via Custom Actions, Webhook.site supports the service Pushed, which has a free tier. More info here.
Alternatively, at least on iPhones, you can use the Send Email custom action and mark the sender address as VIP. This will trigger a push notification when the email is received.
"},{"location":"faq.html#can-i-pay-via-invoice","title":"Can I pay via invoice?","text":"After you've paid for and created a subscription, you will receive an invoice via email.
If you are an enterprise customer that wishes to buy a subscription via bank transfer, please contact Support.
"},{"location":"faq.html#how-do-i-add-my-company-name-to-my-invoice","title":"How do I add my company name to my invoice?","text":"Go to Control Panel -> Billing, then click View next to an invoice and finally click the \"Add Address & VAT Number\" link.
"},{"location":"faq.html#how-can-i-see-the-pricing-in-my-currency","title":"How can I see the pricing in my currency?","text":"Once you start the checkout process, you will see the amount in your local currency, including any applicable VAT.
"},{"location":"faq.html#the-json-data-is-in-a-weird-formatcant-be-parsed-by-extract-jsonpath","title":"The JSON data is in a weird format/can't be parsed by Extract JSONPath","text":"The JSON data might have been attached to the request as form data rather than as request body data, which is usually how JSON is sent.
The data might look like this on Webhook.site:
To remediate this in Extract JSONPath, you'll need to set the source field to the form field variable, which is automatically set by Webhook.site. In the screenshot above, the variable name would be $request.form.my_json_data$
, which works with Extract JSONPath:
If you use any variables in the JSON that could contain e.g. new lines or quote characters, these characters need to be \"escaped\" properly so that the JSON remains valid.
Webhook.site provides an easy way to do this with the .json
Variable Modifier, which will automatically escape any special JSON characters. More info here.
Before:
{\n\"message\": \"$request.query.message$\"\n}\n
After, with the JSON Escape Variable Modifier:
{\n\"message\": \"$request.query.message.json$\"\n}\n
"},{"location":"faq.html#how-do-i-export-the-data-stored-on-webhooksite","title":"How do I export the data stored on Webhook.site?","text":"With Webhook.site Pro, there's a variety of different ways to export data sent to your URL or email address.
Custom Actions can be used in a variety of ways to export data. Below are listed a few examples of actions that could be used. More info here.
Webhook.site provides a CSV Export functionality, simply click the button in the menu to download all data as a CSV file.
Data can be retrieved and saved using the Webhook.site API using any programming language.
With the Webhook.site CLI (Command-Line Interface), requests can be forwarded directly from Webhook.site to a local workstation or server. More info here
There's several ways to accomplish this depending on your needs.
You can periodically fetch the data using the Webhook.site API
Requests can also be streamed to a local URL using the Webhook.site CLI, in a similar fashion to e.g. ngrok.
Webhook.site also supports the XHR Redirect feature, which uses your browser in order to forward the requests. The endpoint will need to respond with CORS headers in all requests so that the browser will be able to send requests to it. The forwarding will only work as long as the browser window is open.
The following CORS headers should allow Webhook.site to forward requests to your local endpoint via XHR Redirect:
Access-Control-Allow-Origin: *\nAccess-Control-Allow-Methods: *\nAccess-Control-Allow-Headers: *\nAccess-Control-Expose-Headers: Content-Length,Content-Range\n
"},{"location":"faq.html#how-can-i-test-an-ipv6-only-request","title":"How can I test an IPv6-only request?","text":"If you access Webhook.site with the hostname https://ipv6.webhook.site, your URL will only work with IPv6.
To use it with a URL, simply replace the webhook.site
hostname with ipv6.webhook.site
.
Using the free version of Webhook.site, URLs automatically expire in 7 days. After that, the URL is no longer available and data is deleted.
With the paid version, Webhook.site Pro, URLs never expire automatically.
"},{"location":"faq.html#im-getting-a-405-method-not-allowed-whats-wrong","title":"I'm getting a 405 Method Not Allowed, what's wrong?","text":"You might be copying the URL for the Webhook.site application (by copying the link from your browsers' address bar), and not the actual URL.
Webhook.site app (\u26d4\ufe0f wrong):https://webhook.site/#!/view/6dbb3859-4ad5-4e85-acae-e44d6e37ea4a
Webhook.site url (\u2705 correct):https://webhook.site/6dbb3859-4ad5-4e85-acae-e44d6e37ea4a
First, make sure that you have copied the correct URL, see here.
For free users: The URL has met the limit of the amount of requests it can receive. To unlock more requests, the URL must be associated with a Webhook.site Pro or Enterprise account. To associate a URL to your account, click Upgrade in the upper-right corner when logged in.
For Webhook.site Pro or Enterprise users: If you're getting this error with a Webhook.site URL, the URL may have been automatically blocked due to an extraordinarly large amount of requests, as per the Fair Use guidelines in our Terms of Service. This is done to prevent a decrease in service level for our other customers. For more info, or to request a whitelisting, please contact Support. If you're getting this error with a Webhook.site API endpoint, you have exceeded the API endpoint quota, and should implement e.g. a backoff strategy.
"},{"location":"faq.html#im-getting-a-413-payload-too-large-whats-wrong-whats-the-request-size-limit","title":"I'm getting a 413 Payload Too Large, what's wrong? / What's the request size limit?","text":"The HTTP body data (e.g. files or JSON data) submitted to Webhook.site must be below 10 megabytes. More than that will cause a HTTP 413 response.
"},{"location":"faq.html#im-getting-an-access-control-check-error","title":"I'm getting an \"Access Control Check error\"","text":"If you're requesting the Webhook.site endpoint from another domain via JavaScript, you'll need to enable CORS so the browser allows the request.
To do this, click Edit in the upper-right corner, check the Add CORS headers checkbox, and click Save.
"},{"location":"faq.html#im-getting-a-certificate-expired-error","title":"I'm getting a \"Certificate Expired\" error","text":"Our SSL certificate is fully working; the issue lies with your system. In september 2021, our SSL provider, LetsEncrypt, updated their root certificate. This can mean that if your locally installed trusted root certificates are of an old version, you'll be seeing a certificate error as Webhook.site now runs a certificate that is based on the new root certificate chain.
To remediate this problem, you'll need to update your local certificate trust store:
update-ca-certificates
; more info here.yum update ca-certificates
; more info hereSome systems and packages, like Python certifi/urllib3, also come with static certificates. We cannot support updating these.
"},{"location":"faq.html#what-is-a-dnshook","title":"What is a DNSHook?","text":"For more information about DNSHook, please see here.
"},{"location":"jobs.html","title":"Webhook.site Jobs","text":"Webhook.site is a small startup that's fully remote. We believe in absolute trust in our employees. What's important to us is your work output and communication, not how many hours you spend. You'll be working at a company with a lot of dedicated and passionate customers that use the product to solve real problems in their work.
"},{"location":"jobs.html#support-engineer-part-time","title":"Support Engineer (part time)","text":""},{"location":"jobs.html#job-roles","title":"Job Roles","text":"Customers use Webhook.site to solve a wide variety of problems, and it's your job to help them use Webhook.site to complete their goals.
Users can be at the beginner to intermediate stage in terms of their understanding of Internet technology, scripting etc., and often you\u2019ll need to sense what the actual problem is\u2013which can be wildly different from what the support ticket is about.
100% remote. Around 10 hours per week.
To apply, send an email to jobs@webhook.site. We expect you to send us a CV along with a short email describing yourself, and then we'll do a half-hour video call to talk about the job and any questions you may have. If we decide to hire you, you'll be able to start as soon as you like.
"},{"location":"jobs.html#full-stack-developer-part-timefull-time","title":"Full-Stack Developer (part time/full time)","text":""},{"location":"jobs.html#job-roles_1","title":"Job Roles","text":"Webhook.site is a complex Laravel and Angular.js based system with more than 100.000 monthly unique users which use it for everything from Webhook API development to workflow automation using Webhook.site\u2019s Custom Actions feature. Much of the site is a REST API with an Angular.js client in addition to some Blade pages.
Your job is to continuously improve on the codebase, whether it\u2019s code organisation, framework and dependency updates, implementing new features and fixing bug tickets.
The job is paid hourly, and you\u2019ll be 100% free to schedule your work as you see fit and work as may or as few hours as you like.
To apply, send an email to jobs@webhook.site. We expect you to send us a CV along with a short email describing yourself, and then we'll do a half-hour video call to talk about the job and any questions you may have. If we decide to hire you, you'll be able to start as soon as you like.
"},{"location":"news.html","title":"News","text":"Subscribe below to receive updates about improvements and new features on Webhook.site as well as infrastructure changes like new IP addresses for e.g. firewall whitelisting. Expect a newsletter a few times a year at most.
"},{"location":"news.html#9-april-2024","title":"9 April 2024","text":"$request.query$
Base Variable not being generated.gzip
encoded request bodies.faker
library added to the JavaScript Custom Action. More info here\\n
as a newline, allowing splitting strings by line.$request.path$
(contains any subpath after the Webhook.site URL ID) and $request.query$
(contains the query string) variables added. More info heredate_from
, date_to
, and query
parameters. More info here.Forward
mode.http()
, which replaces the request()
function and allows for more flexibility as well as sending e.g. form/multipart requests as the new HTTP Request Custom Action also allows. More info hereimage/*
, video/*
and application/pdf
are now shown as linked files in the file list, not binary data.ipv6.webhook.site
hostname. More info heremultipart()
, for sending HTTP multipart requests. More info here.xml2array()
logs more helpful errors.xml2array()
. More info here.string_random()
, uuid()
. More info here.sign()
, digest()
, verify()
. More info here. JWT example here.base64url_encode()
, base64url_decode()
. More info here.array_splice
and array_range
functions. More info herexpath
functions would not match correctly in all cases.is_empty()
More info hereis_null()
- handy for checking if a value is null without type checking issues. More info here429 Too Many Requests
response.false
by default when this third parameter is not set. More info here$request.ip$
) is still the default key.$request.file.[name].id$
and $request.file.[name].link$
(which returns direct link to download the file.)num2hex
, hex2num
and hex2bin
functions.group_id
parameter when creating or updating Tokens./token/:tokenId/requests
are now throttled at 60 requests/minute.Webhook.site/1.0
, which can be overwrited if specifying a User-Agent header manually.Invalid namespace
errors..html_decode
, .url_encode
, and .url_decode
. More info hereclone_from
option when using the API to create Tokens (URLs)json_decode
/ json_encode
now output error messages if they fail due to e.g. bad data.array_sort
, array_join
and json_escape
functions.array_keys
and array_values
functions. For more information, see here.timeout
parameter can also be given to the request()
function in WebhookScript. Both places accept a value in seconds, with decimals.override
parameter to request(), which prevents content from the source request from being included.var()
function..data[?(@.Employee.FirstName)]
.to_date
and date_format
functions have been improved with timezone handling capabilities.trim(string)
, which removes space, newline and tab characters from the beginning and end of a string, similar to PHP's own trim() function.action(action_type, parameters)
, to run Custom Actions inside a WebhookScript. More info here.files()
retrieves a an array of all files, file_content(fileId)
returns the content of a specific file. More info here.request.file.<formname>.content
, request.file.<formname>.size
, request.file.<formname>.filename
csv_to_array()
, which converts a CSV file from a string to an array that can easily be parsed.to_date()
, date_format()
, date_to_array()
, date_interval()
, date_interval_human()
. Read more in the docsdelay()
, which lets you delay and execute WebhookScript code a set amount of seconds in the future.exec()
, which lets you dynamically execute code in a string.import()
, which downloads and executes code from a URL - great if you want to reuse your code, just put it on Github and import it with the URL!base64_encode()
, base64_decode()
.regex_extract
and regex_extract_first
functions.hash
function.url_encode
, url_decode
, and query
functions.\\\\n
) in strings, escaped by 2 backslashes.Global Variables are now available in the Control Panel, which lets you keep configuration like API keys in a separate place from your Custom Actions and scripts while managing them at a central place.
Changed the font of the WebhookScript editor, which resulted in uneven selection of text.
There are two separate editions of Webhook.site:
The code for the completely open-source, MIT-licensed version described on this page is available on https://github.com/webhooksite/webhook.site, and can be self-hosted using e.g. Docker, is great for testing Webhooks, but doesn't include Webhook.site Pro features like Custom Actions.
The cloud version of Webhook.site at https://webhook.site which has more features, some of them requiring a paid subscription.
You can choose to run the Open Source version of Webhook.site either via Docker, or install it on any Web server with PHP7 support.
"},{"location":"open-source.html#realtime-updates","title":"Realtime updates","text":"laravel-echo-server or Pusher can be used to enable realtime updates. Take a look at the .env.example
to see the environment variables required to configure it.
For laravel-echo-server
, the app expects socket.io to be available at the /socket.io
path relative to the index page. This can be done with nginx like so:
location /socket.io {\n proxy_pass http://127.0.0.1:6001;\n proxy_http_version 1.1;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection 'upgrade';\n proxy_set_header Host $host;\n proxy_cache_bypass $http_upgrade;\n }\n
"},{"location":"open-source.html#installation","title":"Installation","text":""},{"location":"open-source.html#docker-recommended","title":"Docker (Recommended)","text":"The provided Docker Compose file sets up a complete environment that runs the Webhook.site image and all dependencies (Redis, Laravel Echo Server, etc.). Note that if running this in production, you should probably run a Redis server that persists data to disk. The Docker image is also not tuned for large amounts of traffic.
"},{"location":"open-source.html#installation-guide","title":"Installation Guide","text":"docker-compose up
A set of Kubernetes configuration files can be found in the kubernetes
subfolder.
Configure the resources, and apply with kubectl apply -f ./
.
DigitalOcean has a guide on how to configure nginx.
"},{"location":"open-source.html#installation-guide_2","title":"Installation Guide","text":"composer install
cp .env.example .env
- adjust settings as neededphp artisan key:generate
/public
folder. Webhook.site Pro was formerly known as Premium. For more information, see here.
"},{"location":"pro.html","title":"Webhook.site Basic, Pro and Enterprise","text":"With Webhook.site Basic, Pro and Webhook.site Enterprise subscriptions, you get more features that allows you to do more with your URL, as well as keep the data contained private and secure.
Free Basic Pro Enterprise URLs n/a 1 Unlimited Unlimited Schedules n/a 1 Unlimited Unlimited Custom Actions \u274c \u2705 \u2705 \u2705 Schedules \u274c \u2705 \u2705 \u2705 Custom Addresses \u274c \u2705 \u2705 \u2705 Secure Data \u274c \u2705 \u2705 \u2705 Permanent URLs \u274c \u2705 \u2705 \u2705 CSV Export \u274c \u2705 \u2705 \u2705 Free Email Support \u274c \u2705 \u2705 \u2705 Custom Domain \u274c \u274c \u274c \u2705 Multi-User Support \u274c \u274c \u274c \u2705 Request History 100 (hard limit) 10.0002 10.0002 10.0002 URL/Email Address expiry 7 days1 Never expires Never expires Never expires Data storage duration 7 days1 365 days 365 days 365 days Pricing3 $9/month $90/year (save 20%) $18/month $180/year (save 20%) $49/month $499/year (save 20%) Subscribe NowExtract data, send emails and HTTP requests, upload files, run scripts and much more with Custom Actions that are run on each incoming request or email, with the Webhook.site Custom Actions builder.
Native integrations for AWS, Google Sheets, Slack, Dropbox, MySQL and others.
Read more
"},{"location":"pro.html#schedules","title":"Schedules","text":"Use Schedules by Webhook.site to send requests any URL or run your Custom Actions automatically on an interval. Use it for monitoring your Web site, clearing caches, and much more. Supports cron
syntax, in addition to predefined intervals.
Read more
"},{"location":"pro.html#custom-urls-and-emails","title":"Custom URLs and Emails","text":"Get an address that's easy to remember:
webhook.site/my-alias
my-alias@email.webhook.site
(With Webhook.site Enterprise, you can even use your own domain name.)
"},{"location":"pro.html#security","title":"Security","text":"Your data is secured in your account, and can be accessed by API Key or login. With the free version of Webhook.site, others can freely view the requests sent to your URL.
"},{"location":"pro.html#permanent-urls","title":"Permanent URLs","text":"The URLs stay in your account forever, the 7-day auto expiration of the free version is removed.
"},{"location":"pro.html#store-more-than-500-requests-or-emails","title":"Store more than 500 requests or emails","text":"With the free version, URLs stop taking in new requests or emails after the 500 request limit is reached. With Pro, the limit of max 500 requests or emails per URL is removed and raised to 10.000 requests or emails, which are then rotated automatically.
"},{"location":"pro.html#unlimited-urls","title":"Unlimited URLs","text":"Create as many upgraded URLs or email addresses as you need at no extra cost.
"},{"location":"pro.html#csv-export","title":"CSV Export","text":"Export all requests and emails to a CSV file.
"},{"location":"pro.html#localhost-forwarding","title":"Localhost Forwarding","text":"Forward requests to your Webhook.site URL to your local network via Webhook.site CLI.
"},{"location":"pro.html#email-support","title":"Email Support","text":"We\u2019ll help you troubleshoot and set up your Custom Actions and workflows.
"},{"location":"pro.html#enterprise-features","title":"Enterprise features","text":"In addition to all Pro features, Enterprise also contains the following features:
"},{"location":"pro.html#custom-domain","title":"Custom Domain","text":"Use a your own (sub)domain for URLs and emails:
mydomain.com/my-alias
my-alias@email.mydomain.com
Additionally, Webhook.site provides an SSL certificate which is included free of charge with your Enterprise subscription.
"},{"location":"pro.html#multi-user-support","title":"Multi-User Support","text":"Ideal for larger organizations, share URLs, schedules, and other data with the rest of the organization. Configure individual users' access with User Types like Administrator and Viewer, so some users may only view the content.
"},{"location":"schedules.html","title":"Webhook.site Schedules","text":"Included in your Webhook.site Pro subscription is Webhook.site Schedules, which enables you to periodically send requests to specified URLs (also including your Webhook.site URLs, so your Custom Actions can be executed periodially) with a custom method, headers, and interval.
Schedules can be used for a variety of purposes, including cache warming, uptime monitoring, automatic data transfer, etc.
After creating the Schedule, you can view the logs for the last 100 scheduled requests.
Per default, the timeout for the Schedule requests is 5 seconds, but can range from 1 to 30 seconds.
A timeout or server error will trigger a Schedule error notification email, if enabled in Control Panel -> Settings.
Schedules can also be managed using the Schedules API.
"},{"location":"schedules.html#uptime-monitoring-requirements","title":"Uptime monitoring & Requirements","text":"If you create a Schedule and enable the When an error happens during a Schedule run setting, Webhook.site will automatically send an email notification when a URL has a timeout longer than the one specified, or when one of the requirements aren't met. It is possible to specify requirements for the response body containing a string, and the response status code being within a certain range.
"},{"location":"schedules.html#schedule-intervals","title":"Schedule Intervals","text":"In addition to be able to use a custom cron
-style expression string, Schedules can be executed at the following preset intervals:
Schedule intervals are based on UTC time.
For building cron-style expressions, we recommend https://crontab.guru.
"},{"location":"api/about.html","title":"Webhook.site API","text":"The Webhook.site API is public, free to use, doesn't require authentication and is relatively easy to use.
"},{"location":"api/about.html#general-usage","title":"General Usage","text":"Base URL: https://webhook.site
.
You must set the Accept
and Content-Type
headers to application/json
.
:
(colon) to show which parameters must be changed by the user. You must not include this character in the URL.https://webhook.site/00000000-0000-0000-0000-000000000000
, the Token ID is then 00000000-0000-0000-0000-000000000000
.Api-Key
HTTP header.While many endpoints of the Webhook.site API are public and work without any authentication, some endpoints do require authentication, or will return a 401 Unauthorized
status code.
An API Key can be generated in the Control Panel, and provides access to Tokens that are either a) password protected or b) require login.
API Keys must be specified in the Api-Key
HTTP header.
If you have set a password on a Webhook.site URL/token, to access the API resources for that token, you can use either of the following methods:
password
query string: ?password=[your password]
To retrieve the data that's sent to a Webhook.site URL or Email, you'll want to use the Get Requests endpoint.
"},{"location":"api/about.html#create-new-urlemail-address","title":"Create new URL/email address","text":"To create a new token programmatically, you can use the API like this:
$ curl -X POST https://webhook.site/token\n
This will return information about the token in JSON format, including its UUID. Your URL will be available at the endpoint https://webhook.site/[token uuid]
.
If you are a Webhook.site Pro or Enterprise customer, you should provide an API key in order to associate the created token with your account automatically:
$ curl -X POST -H 'Api-Key: 00000000-0000-0000-0000-000000000000' https://webhook.site/token\n
More info here.
"},{"location":"api/action-types.html","title":"Action types","text":"The following is a list of the API names for Action Types, along with a list of parameters, and their validation requirements.
"},{"location":"api/action-types.html#auto_json","title":"auto_json
","text":"source
: stringjsonpath
: stringvariable_name
: required, stringaws_cf_invalidate
","text":"provider_id
: required, intdistribution_id
: required, stringpaths
: required, stringaws_s3_create_bucket
","text":"provider_id
: string, requiredregion
: string, requiredbucket_name
: string, requiredcanned_acl
: string, in:private,public-read,public-read-write,authenticated-readaws_s3_delete_object
","text":"provider_id
: string, requiredregion
: stringbucket_name
: string, requiredobject_key
: string, requiredaws_s3_get_object
","text":"provider_id
: string, requiredregion
: stringbucket_name
: string, requiredobject_key
: string, requiredvariable_name
: string, required, min:1aws_s3_put_object
","text":"provider_id
: string, requiredregion
: string, requiredbucket_name
: stringobject_key
: string, requiredbody
: string, requiredcanned_acl
: string, in:private,public-read,public-read-write,authenticated-readcondition
","text":"input
: stringoperator
: required, string, in:eq,neq,sw,ew,ct,nct,gt,gte,lt,ltevalue
: stringaction
: required, string, in:stop,continue,noopconditions
","text":"conditions
: required, arraymode
: required, string, in:one,all,noneaction
: required, string, in:stop,continue,noopdatabase
","text":"type
: required, in:mysql,pgsqlhost
: required, stringport
: number, min:1, max:65535database
: required, stringpassword
: stringusername
: required, stringstatement
: required, stringparams
: arrayvariable_name
: stringcharset
: stringdiscord_send_message
","text":"provider_id
: required, stringcontent
: required, stringusername
: stringavatar_url
: urlembed_type
: string, in:link,image,videoembed_url
: urldont_save
","text":"No parameters for dont_save
.
dropbox_create_folder
","text":"provider_id
: string, requiredpath
: string, requireddropbox_delete
","text":"provider_id
: string, requiredpath
: string, requireddropbox_download_file
","text":"provider_id
: string, requiredpath
: string, requiredvariable_name
: string, requireddropbox_get_link
","text":"provider_id
: string, requiredpath
: string, requiredvariable_name
: string, requireddropbox_upload_file
","text":"provider_id
: string, requiredpath
: string, requiredbody
: string, requiredmode
: string, required, in:add,overwrite,updateextract_jsonpath
","text":"jsonpath
: required, stringvariable_name
: required, stringsource
: stringdefault
: stringrepeat
: booleanextract_regex
","text":"regex
: required, stringvariable_name
: required, stringsource
: stringdefault
: stringrepeat
: booleanextract_xpath
","text":"xpath
: required, stringvariable_name
: required, stringsource
: stringdefault
: stringftp_upload
","text":"host
: required, stringport
: number, min:1, max:65535password
: required, stringusername
: required, stringpath
: required, stringcontent
: required, stringssl
: boolpassive
: boolgoogle_sheets_add_row
","text":"provider_id
: string, requiredspreadsheet_id
: string, requiredrange
: string, requiredvalues
: string, requiredformula_mode
: boolgoogle_sheets_get_values
","text":"provider_id
: string, requiredspreadsheet_id
: string, requiredrange
: string, requiredvariable_name
: required, stringgoogle_sheets_update_row
","text":"provider_id
: string, requiredspreadsheet_id
: string, requiredrange
: string, requiredvalues
: string, requiredformula_mode
: boolhttp
","text":"url
: required, stringcontent
: nullable, stringmethod
: nullable, in:POST,GET,OPTIONS,PUT,DELETE,PATCH,TRACEmode
: nullable, in:text,json,multipart,urlencoded,forwardauth
: nullable, objectauth.mode
: string, in:basic,digest,ntlmauth.username
: stringauth.password
: stringmultipart
: array, required_if:mode,multipartmultipart.*.name
: stringmultipart.*.filename
: stringmultipart.*.content-type
: stringmultipart.*.content
: stringurlencoded
: arrayurlencoded.*.name
: string, required_if:mode,urlencodedurlencoded.*.value
: string, required_if:mode,urlencodedheaders
: nullable, stringskip_ssl_verification
: nullable, boolvariable_name
: stringtimeout
: nullable, numeric, max:15image_resize
","text":"source
: string, requiredwidth
: string, required_without:heightheight
: string, required_without:widthaspect_ratio
: bool, requiredvariable_name
: string, requiredjavascript
","text":"script
: required, stringlog
","text":"text
: required, stringmicrosoft_drive_download
","text":"provider_id
: required, stringpath
: required, stringvariable_name
: stringmicrosoft_drive_upload
","text":"provider_id
: required, stringpath
: required, stringcontent_type
: stringcontent
: stringvariable_name
: stringmicrosoft_excel_add_rows
","text":"provider_id
: required, stringpath
: stringtable
: stringindex
: intvalues
: required, arraymicrosoft_excel_get_values
","text":"provider_id
: required, stringpath
: required, stringworksheet
: required, stringrange
: required, stringvariable_name
: required, stringmodify_response
","text":"content
: stringstatus
: stringheaders
: stringstop
: boolntfy
","text":"topic
: string, requiredtitle
: stringmessage
: string, requiredpushed_send
","text":"app_key
: string, requiredapp_secret
: string, requiredtarget_type
: string, requiredtarget
: string, requiredmessage
: string, requiredrabbitmq_get
","text":"host
: string, requiredport
: intusername
: string, requiredpassword
: string, requiredvhost
: stringqueue
: string, requiredssl
: booleanvariable_name
: stringrabbitmq_publish
","text":"host
: string, requiredport
: intusername
: string, requiredpassword
: string, requiredvhost
: stringqueue
: string, requiredssl
: booleanmessage
: string, requiredrate_limit
","text":"period
: required, intcount
: required, intkey
: stringscript
","text":"script
: required, stringsend_email
","text":"sender
: stringrecipient
: required, stringcontent
: stringis_html
: booleansubject
: required, stringattachments
: arraysend_email_smtp
","text":"sender_name
: stringsender_email
: stringrecipient
: required, stringcontent
: stringis_html
: booleansubject
: required, stringencryption
: string, in:none,ssl,tlsport
: intusername
: string, requiredpassword
: string, requiredhost
: string_requiredattachments
: arraysend_request
","text":"url
: required, stringcontent
: nullable, stringmethod
: nullable, in:POST,GET,OPTIONS,PUT,DELETEheaders
: nullable, stringskip_ssl_verification
: nullable, boolvariable_name
: stringtimeout
: nullable, numeric, max:30set_variable
","text":"name
: required, stringvalue
: nullable, stringsftp_upload
","text":"provider_id
: stringhost
: required, stringport
: number, min:1, max:65535username
: required, stringpassword
: stringpath
: required, stringcontent
: required, stringslack_send_message
","text":"webhook_url
: required, urlraw
: boolcontent
: required, stringssh_run_command
","text":"provider_id
: stringhost
: required, stringport
: number, min:1, max:65535username
: required, stringpassword
: stringcommand
: required, stringvariable_name
: stringstop
","text":"No parameters for stop
.
store_global_variable
","text":"name
: required, stringvalue
: nullable, stringtemplate
","text":"template_id
: required, intvariables
: arraytext_map
","text":"source
: required, stringoperator
: required, stringvariable_name
: required, stringdefault
: required, stringmappings
: required, arraytext_replace
","text":"source
: required, stringvariable_name
: required, stringreplacements
: required, arraytext_split
","text":"delimiter
: required, stringsource
: required, stringvariable_name
: required, stringrepeat
: booleantwitter_tweet
","text":"provider_id
: required, stringtweet
: required, stringThe Custom Actions API allows you to manage the Custom Actions associated with a given Token. More info about Custom Actions.
List of the API names and parameters for Action Types.
"},{"location":"api/custom-actions.html#actions","title":"Actions","text":""},{"location":"api/custom-actions.html#create-custom-action","title":"Create Custom Action","text":"POST /token/:token_id/actions
type
(string) is the name of an Action Type.order
(int) specified which order the action is executed in.parameters
(object) can vary depending on the Action Type. disabled
(bool) if set to true, the action is skipped upon execution.queue
(bool) If set to true, the action is run asynchronously. More info heredelay
(int, max 86400) If set (along with queue
to true), Webhook.site waits the specified amount of seconds to run the action.condition
(uuid) If set to an ID of a Conditions action, the action will only run if the condition passes, and is otherwise skipped.{\n\"type\": \"condition\",\n\"order\": 3,\n\"disabled\": false,\n\"parameters\": {\n\"input\": \"$request.content$\",\n\"operator\": \"eq\",\n\"value\": \"\",\n\"action\": \"stop\"\n}\n}\n
"},{"location":"api/custom-actions.html#example-2-webhookscript-action","title":"Example 2: WebhookScript action","text":"{\n\"type\": \"script\",\n\"order\": 1,\n\"parameters\": {\n\"script\": \"expiry = '2021-08-01T00:00:00.000000Z'\\nnow = to_date('now')\\n\\nif (date_interval(now, expiry) < 0) {\\n // Respond with 410 Gone\\n respond('This content is no longer available.', 410)\\n}\\n\"\n}\n}\n
"},{"location":"api/custom-actions.html#example-3-creating-webhookscript-action-with-python-3","title":"Example 3: Creating WebhookScript action with Python 3","text":"Same script as Example 2. Requires the requests
module, which can be installed using pip install requests
.
import requests\nscript = \"\"\"\nexpiry = '2021-08-01T00:00:00.000000Z'\nnow = to_date('now')\nif (date_interval(now, expiry) < 0) {\n // Respond with 410 Gone\n respond('This content is no longer available.', 410)\n}\n\"\"\"\ndata = {\n\"type\": \"script\",\n\"order\": 1,\n\"parameters\": {\n\"script\": script\n}\n}\nr = requests.post('https://webhook.site/token/7d63959e-4fec-49bd-90dc-a4615722825e/actions', json=data)\n
"},{"location":"api/custom-actions.html#response","title":"Response","text":"{\n\"uuid\": \"7ae324d6-c65b-416b-8f83-18fb89e0c740\",\n\"token_id\": \"fe18d303-631d-4620-acb3-5c0b1b0b876d\",\n\"type\": \"condition\",\n\"order\": 3,\n\"disabled\": null,\n\"parameters\": {\n\"input\": \"$request.content$\",\n\"operator\": \"eq\",\n\"value\": \"\",\n\"action\": \"stop\"\n}\n}\n
"},{"location":"api/custom-actions.html#get-custom-actions","title":"Get Custom Actions","text":"GET /token/:token_id/actions
200 OK
{\n\"data\": [\n{\n\"uuid\": \"52055928-099a-44dc-ba31-e8d808b98ea1\",\n\"token_id\": \"fe18d303-631d-4620-acb3-5c0b1b0b876d\",\n\"type\": \"condition\",\n\"order\": 1,\n\"disabled\": false,\n\"parameters\": {\n\"input\": \"$request.header.content-type$\",\n\"operator\": \"nct\",\n\"value\": \"application/json\",\n\"action\": \"stop\"\n}\n},\n{\n\"uuid\": \"27b07ca7-ea83-48f5-b376-2372cf25d3a1\",\n\"token_id\": \"fe18d303-631d-4620-acb3-5c0b1b0b876d\",\n\"type\": \"condition\",\n\"order\": 2,\n\"disabled\": null,\n\"parameters\": {\n\"input\": \"$request.content$\",\n\"operator\": \"eq\",\n\"value\": \"\",\n\"action\": \"stop\"\n}\n}\n]\n}\n
"},{"location":"api/custom-actions.html#update-custom-action","title":"Update Custom Action","text":"PUT /token/:token_id/actions/:action_id
See Create Custom Action endpoint.
"},{"location":"api/custom-actions.html#response_2","title":"Response","text":"See Create Custom Action endpoint.
"},{"location":"api/custom-actions.html#test-custom-action","title":"Test Custom Action","text":"POST /token/:token_id/test-action
request_id
: A request ID to base the test run on. If not set, uses default request variables.action_id
: When set, overwrites the parameters of an existing action. If not, tests a temporary new, empty action with ID 00000000-0000-4000-0000-000000000000
.{\n\"type\": \"script\",\n\"order\": 2,\n\"parameters\": {\n\"script\": \"echo('hello world')\"\n}\n}\n
"},{"location":"api/custom-actions.html#response_3","title":"Response","text":"200 OK
{\n\"success\": true,\n\"result\": {\n\"output\": {\n\"08529a4f-ad84-450b-977a-1d126d6ca6b7\": [\n\"Set runtime variable $aaa$ to \\\"example\\\"\"\n],\n\"00000000-0000-4000-0000-000000000000\": [\n\"hello world\"\n]\n},\n\"response\": {\n\"content\": null,\n\"status\": null,\n\"headers\": null\n},\n\"variables\": {\n\"request.header.content-length\": \"57362\",\n\"request.header.user-agent\": \"Paw/3.3.5 (Macintosh; OS X/11.6.2) GCDHTTPRequest\",\n\"request.header.connection\": \"close\",\n\"request.header.host\": \"webhook.site\",\n\"request.header.content-type\": \"application/json\",\n\"request.uuid\": \"87240a26-1426-45dd-9b4c-961a323652a9\",\n\"request.token_id\": \"7fc77812-9efe-41b6-9365-e2c1fb5feb62\",\n\"request.content\": \"\",\n\"request.date\": \"2022-03-20 10:18:58\",\n\"request.timestamp\": 1647771538,\n\"request.hostname\": \"webhook.site\",\n\"request.size\": 0,\n\"request.type\": \"web\",\n\"request.ip\": \"86.52.35.76\",\n\"request.user_agent\": \"Paw/3.3.5 (Macintosh; OS X/11.6.2) GCDHTTPRequest\",\n\"request.url\": \"https://webhook.site/7fc77812-9efe-41b6-9365-e2c1fb5feb62\",\n\"request.method\": \"POST\",\n\"aaa\": \"example\"\n}\n}\n}\n
"},{"location":"api/custom-actions.html#delete-custom-action","title":"Delete Custom Action","text":"DELETE /token/:token_id/actions/:action_id
PUT /token/:token_id/actions/toggle
This endpoint toggles whether actions are enabled on a specific token.
"},{"location":"api/custom-actions.html#response_4","title":"Response","text":"200 OK
{\n\"enabled\": true\n}\n
"},{"location":"api/date-expressions.html","title":"API Endpoints: Query string date expressions","text":"Date expressions can be used in the query
parameter when retreiving and filtering Requests from the API.
The expression starts with an anchor date, which can either be now
(or *
), or a date string ending with ||
(two pipe characters).
This anchor date can optionally be followed by one or more maths expressions:
+1h
\u2013 Add one hour-1d
\u2013 Subtract one day/d
\u2013 Round down to the nearest dayThe supported time units differ from those supported by time units for durations. The supported units are:
Symbol Meaning y Years M Months w Weeks d Days h Hours H Hours m Minutes s SecondsAssuming now is 2001-01-01 12:00:00, some examples are:
Example Resolves to now+1h now in milliseconds plus one hour. Resolves to: 2001-01-01 13:00:00 now-1h now in milliseconds minus one hour. Resolves to: 2001-01-01 11:00:00 now-1h/d now in milliseconds minus one hour, rounded down to UTC 00:00. Resolves to: 2001-01-01 00:00:00 2001.02.01||+1M/d 2001-02-01 in milliseconds plus one month. Resolves to: 2001-03-01 00:00:00"},{"location":"api/examples.html","title":"API Examples","text":"If you have a Webhook.site account, before using the API, please first create an API key here. The examples below can also be used without an account, but in that case you should remove the API key header.
Don't have an account yet? Sign up here.
"},{"location":"api/examples.html#curl","title":"cURL","text":""},{"location":"api/examples.html#send-json-data-to-url","title":"Send JSON data to URL","text":"curl -X POST 'https://webhook.site/00000000-0000-0000-0000-000000000000' \\\n-H 'content-type: application/json' \\\n-d $'{\"id\": 7, \"name\": \"Jack Daniels\", \"position\": \"Assistant\"}'\n
"},{"location":"api/examples.html#get-data-of-last-request-sent-to-url","title":"Get data of last request sent to URL","text":"curl 'https://webhook.site/token/00000000-0000-0000-0000-000000000000/request/latest/raw' \\\n-H 'accept: application/json' \\\n-H 'api-key: 00000000-0000-0000-0000-000000000000'\n
Output:
{\"id\": 7, \"name\": \"Jack Daniels\", \"position\": \"Assistant\"}\n
"},{"location":"api/examples.html#send-file-to-url","title":"Send file to URL","text":"Uploads the file example.png
from the current directory.
curl -F 'file=@example.png' https://webhook.site/00000000-0000-0000-0000-000000000000\n
To download the file, click the Download link in the Webhook.site interface, or via the API, use the download endpoint.
"},{"location":"api/examples.html#python","title":"Python","text":""},{"location":"api/examples.html#fetch-latest-data","title":"Fetch latest data","text":"Requires the requests
module, which can be installed using pip install requests
.
Prints the 50 latest requests sent to a given URL to console.
import requests\ntoken_id = \"00000000-0000-0000-0000-000000000000\"\nheaders = {\"api-key\": \"00000000-0000-0000-0000-000000000000\"}\nr = requests.get('https://webhook.site/token/'+ token_id +'/requests?sorting=newest', headers=headers)\nfor request in r.json()['data']:\nprint(request)\n
"},{"location":"api/examples.html#create-urlemail-address","title":"Create URL/Email address","text":"Requires the requests
module, which can be installed using pip install requests
.
You'll also need to replace the API key.
import requests\njson = {\n\"default_status\": 200,\n\"default_content\": \"Hello world!\",\n\"default_content_type\": \"text/html\",\n}\nheaders = {\n\"api-key\": \"00000000-0000-0000-0000-000000000000\"\n}\nr = requests.post('https://webhook.site/token', json=json, headers=headers)\nprint('URL Created: https://webhook.site/' + r.json()['uuid'])\n
"},{"location":"api/examples.html#php","title":"PHP","text":""},{"location":"api/examples.html#create-token-urlemail-address","title":"Create Token (URL/Email address)","text":"Creates a Webhook.site Token and outputs its Web URL. You'll need to replace the API key.
<?php\n$apiKey = '00000000-0000-0000-0000-000000000000';\n// Create a stream context\n$context = stream_context_create(['http' => [\n'method' => 'POST',\n'header' => \"Api-Key: $apiKey\\r\\n\"\n]]);\n// Send API request\n$response = json_decode(file_get_contents('https://webhook.site/token', false, $context), true);\necho \"URL Created: https://webhook.site/{$response['uuid']}\";\n
"},{"location":"api/examples.html#fetch-latest-data_1","title":"Fetch latest data","text":"Simple example of how to loop through the latest requests or emails sent to a Webhook.site URL or email and display in a friendly manner.
You'll need to replace the API key (if you have a Webhook.site account, otherwise leave it out) and token ID.
<?php\n$apiKey = '00000000-0000-0000-0000-000000000000';\n$tokenId = '00000000-0000-0000-0000-000000000000';\n$url = \"https://webhook.site/token/$tokenId/requests?sorting=newest\";\n$context = stream_context_create(['http' => ['header' => \"Api-Key: $apiKey\\r\\n\"]]);\n$response = json_decode(file_get_contents($url, false, $context), true);\nforeach ($response['data'] as $req) {\necho \"\\n\";\necho json_encode([\n'method' => $req['method'],\n'body' => $req['content'],\n'date' => $req['created_at'],\n], \\JSON_PRETTY_PRINT);\n}\n
Example output when running the script after running e.g. curl -X POST https://webhook.site/00000000-0000-0000-0000-000000000000 -d \"Hello world\"
:
{\n \"method\": \"POST\",\n \"body\": \"Hello world\",\n \"date\": \"2023-06-08 13:04:33\"\n}\n{\n \"method\": \"POST\",\n \"body\": \"just testing\",\n \"date\": \"2023-06-08 13:03:57\"\n}\n
"},{"location":"api/examples.html#download-all-request-data-as-files","title":"Download all request data as files","text":"Per default, this script saves the body content of all requests or emails sent to a URL as .json files in the /requests
directory relative to the script file location.
<?php\n$directory = __DIR__ . '/requests';\n//$apiKey = '00000000-0000-0000-0000-000000000000';\n$tokenId = '00000000-0000-0000-0000-000000000000';\nif (!is_dir($directory) || !is_writable($directory)) {\nthrow new Exception(sprintf('%s not writable, directory missing or rights issue.', $directory));\n}\n$context = stream_context_create(['http' =>\nisset($apiKey) ? ['header' => \"Api-Key: $apiKey\\r\\n\"] : [],\n]);\n$page = 1;\ndo {\n$url = sprintf('https://webhook.site/token/%s/requests?sorting=newest&page=%d', $tokenId, $page);\n$response = json_decode(file_get_contents($url, false, $context), true);\nforeach ($response['data'] as $req) {\nfile_put_contents(\nsprintf('/%s/%s.json', $directory, $req['uuid']),\n$req['content']\n);\necho(sprintf(\n\"[Page %3d] Downloaded request %s sent at %s (%d bytes)\\n\",\n$page, $req['uuid'], $req['created_at'], $req['size']\n));\n}\n$page++;\nsleep(1);\n} while (!$response['is_last_page'] && isset($response['data']));\n
"},{"location":"api/examples.html#nodejs","title":"Node.js","text":""},{"location":"api/examples.html#fetch-latest-data_2","title":"Fetch latest data","text":"This script outputs the data sent to a Webhook.site URL in the last 2 hours.
To do this, the script uses the query
parameter (more info here) in conjunction with a created_at
filter.
Before running the script, you should install dependencies by running npm install axios moment
.
import axios from 'axios';\nimport moment from 'moment';\n// Change these!\nconst apiKey = '00000000-0000-0000-0000-000000000000';\nconst tokenId = '00000000-0000-0000-0000-000000000000';\nasync function getData(apiKey, tokenId) {\nlet date = moment.utc().subtract(2, 'hours').format('YYYY-MM-DD hh:mm:ss'); let dateQuery = `created_at:[\"${date}\" TO \"*\"]`\ntry {\nconst response = await axios.get(`https://webhook.site/token/${tokenId}/requests`, {\nparams: {\nquery: dateQuery,\n},\nheaders: {\n'Api-Key': apiKey,\n'Accept': 'application/json',\n}\n});\nreturn response.data;\n} catch (error) {\nconsole.error(error);\n}\n}\nconst requests = await getData(apiKey, tokenId);\nconsole.log(`${requests.total} requests found:`);\nfor (const request of requests.data) {\nconsole.log({\nmethod: request.method, body: request.content,\ndate: request.created_at,\n});\n}\n
"},{"location":"api/global-variables.html","title":"API Endpoints: Global Variables","text":"With Global Variables, you can store data permanently that can be shared between your URLs. Global Variables can be used when creating Custom Actions and in Schedules. More about Global Variables
"},{"location":"api/global-variables.html#create-global-variable","title":"Create Global Variable","text":"After creating, the variable $name$
will be available in Custom Actions.
POST /global-variables
name
(string) The name of the variable.value
(string) The value of the variable.{\n\"name\": \"content_type\",\n\"value\": \"application/json\"\n}\n
"},{"location":"api/global-variables.html#response","title":"Response","text":"{\n\"id\": 598297,\n\"name\": \"content_type\",\n\"team_id\": 1,\n\"value\": \"application\\/json\",\n\"updated_at\": \"2024-04-15T11:20:02.000000Z\",\n\"created_at\": \"2024-04-15T11:20:02.000000Z\"\n}\n
"},{"location":"api/global-variables.html#get-all-global-variables","title":"Get all Global Variables","text":"GET /global-variables
per_page
(int) - amount of requests returned, defaults to 50 (max 100)page
(int) - page number to retrieve (default 1)search
(string) - filter variables by name{\n\"current_page\": 1,\n\"data\": [\n{\n\"id\": 598297,\n\"user_id\": 0,\n\"team_id\": 1,\n\"name\": \"content_type\",\n\"value\": \"application\\/json\",\n\"created_at\": \"2024-04-15T11:20:02.000000Z\",\n\"updated_at\": \"2024-04-15T11:20:02.000000Z\"\n},\n{\n\"id\": 598294,\n\"user_id\": 0,\n\"team_id\": 1,\n\"name\": \"test\",\n\"value\": \"\\ud83e\\udd72\",\n\"created_at\": \"2024-04-14T13:21:24.000000Z\",\n\"updated_at\": \"2024-04-14T13:21:24.000000Z\"\n}\n],\n\"first_page_url\": \"https:\\/\\/webhook.test\\/global-variables?page=1\",\n\"from\": 1,\n\"last_page\": 21992,\n\"last_page_url\": \"https:\\/\\/webhook.test\\/global-variables?page=21992\",\n\"links\": [\n{\n\"url\": null,\n\"label\": \"« Previous\",\n\"active\": false\n},\n{\n\"url\": \"https:\\/\\/webhook.test\\/global-variables?page=1\",\n\"label\": \"1\",\n\"active\": true\n},\n{\n\"url\": null,\n\"label\": \"...\",\n\"active\": false\n},\n{\n\"url\": \"https:\\/\\/webhook.test\\/global-variables?page=21991\",\n\"label\": \"21991\",\n\"active\": false\n},\n{\n\"url\": \"https:\\/\\/webhook.test\\/global-variables?page=2\",\n\"label\": \"Next »\",\n\"active\": false\n}\n],\n\"next_page_url\": \"https:\\/\\/webhook.test\\/global-variables?page=2\",\n\"path\": \"https:\\/\\/webhook.test\\/global-variables\",\n\"per_page\": 15,\n\"prev_page_url\": null,\n\"to\": 15,\n\"total\": 329870\n}\n
"},{"location":"api/global-variables.html#update-global-variable","title":"Update Global Variable","text":""},{"location":"api/global-variables.html#request_2","title":"Request","text":"PUT /global-variables/:globalVariableId
(See Create Global Variable above for request.)
"},{"location":"api/global-variables.html#response_2","title":"Response","text":"(See Create Global Variable above for response.)
"},{"location":"api/global-variables.html#delete-global-variable","title":"Delete Global Variable","text":""},{"location":"api/global-variables.html#request_3","title":"Request","text":"DELETE /global-variables/:globalVariableId
204 No content
Webhook.site Groups allows you to organize your Tokens. Each Group is a container for multiple Tokens.
"},{"location":"api/groups.html#create-group","title":"Create Group","text":"POST /groups
name
(string) The name of the group.{\n\"name\": \"My Group\"\n}\n
"},{"location":"api/groups.html#response","title":"Response","text":"{\n\"id\": 3498,\n\"team_id\": 12098,\n\"name\": \"My Group\",\n\"updated_at\": \"2023-04-26 18:43:46\",\n\"created_at\": \"2023-04-26 18:43:46\"\n}\n
"},{"location":"api/groups.html#get-all-groups","title":"Get all Groups","text":"GET /groups
{\n\"current_page\": 1,\n\"data\": [\n{\n\"id\": 3498,\n\"team_id\": 12098,\n\"name\": \"My Group\",\n\"created_at\": \"2022-10-26 14:25:11\",\n\"updated_at\": \"2022-10-26 14:25:11\"\n},\n{\n\"id\": 3499,\n\"team_id\": 12098,\n\"name\": \"My nice group\",\n\"created_at\": \"2023-04-26 09:57:40\",\n\"updated_at\": \"2023-04-26 09:57:40\"\n}\n],\n\"first_page_url\": \"https:\\/\\/webhook.site\\/groups?page=1\",\n\"from\": 1,\n\"last_page\": 1,\n\"last_page_url\": \"https:\\/\\/webhook.site\\/groups?page=1\",\n\"next_page_url\": null,\n\"path\": \"https:\\/\\/webhook.site\\/groups\",\n\"per_page\": \"2\",\n\"prev_page_url\": null,\n\"to\": 2,\n\"total\": 2\n}\n
"},{"location":"api/groups.html#update-group","title":"Update Group","text":""},{"location":"api/groups.html#request_2","title":"Request","text":"PUT /groups/:groupId
(See Create Group above for request.)
"},{"location":"api/groups.html#response_2","title":"Response","text":"(See Create Group above for response.)
"},{"location":"api/groups.html#delete-group","title":"Delete Group","text":""},{"location":"api/groups.html#request_3","title":"Request","text":"DELETE /groups/:groupId
204 No content
The Requests API is used to retrieve, manipulate and delete data sent to a given Webhook.site token (URL, E-mail Address or DNSHook.)
"},{"location":"api/requests.html#capture-request","title":"Capture request","text":"any method /:tokenId
any method /:tokenId/:statusCode
any method /:tokenId/(anything)
If statusCode
is valid, that HTTP status will be used in the response (instead of the default.)
Instead of tokenId
, an the alias of the token can also be supplied. Multiple subpaths, e.g. /:tokenId/api/v1/users
, will also be captured.
If the Token has a timeout
value, there is a dynamic rate limit of 100 \u00f7 timeout
requests per minute, e.g. a timeout of 30 allows for 3 requests per minute, and 1 second allows for 100 requests per minute.
The default response (or a response set with e.g. the Modify Response Custom Action) of the Token will be returned.
"},{"location":"api/requests.html#get-requests","title":"Get requests","text":"GET /token/:token_id/requests
Lists all request sent to a token.
"},{"location":"api/requests.html#query-string-parameters","title":"Query string parameters","text":"sorting
(string) - either newest
or oldest
(default)per_page
(int) - amount of requests returned, defaults to 50 (max 100)page
(int) - page number to retrieve (default 1)date_from
, date_to
(date string) - filter requests by date, format yyyy-MM-dd HH:mm:ss
query
(string) - filter requests by a query string search (see below for examples)The following fields can be used to filter via the query
parameter:
uuid
token_id
team_id
type
hostname
size
content
time
created_at
updated_at
custom_action_output
files.[id]
headers.[header]
method
- type web
onlyuser_agent
- type web
onlyurl
- type web
onlyip
- type web
onlyquery.[field]
- type web
onlyrequest.[field]
- type web
only (form fields)sender
- type email
onlytext_content
- type email
onlymessage_id
- type email
onlychecks.[type]
- type email
onlydestinations]
- type email
onlyYou can filter requests by the following syntax:
foobar
- returns requests or emails with body contents containing the word foobar
content:foobar
- returns requests or emails with body contents containing the word foobar
method:GET
- returns all requests with method GETheaders.user-agent:\"Paw/3.3.5 (Macintosh; OS X/11.6.2) GCDHTTPRequest\"
- search value of user-agent headerquery.action:create
- returns requests that have the query string action
set to create
._exists_:query.action
- returns requests where the action query parameter existstype:web
/ type:email
- returns either Web requests or emailstype:web AND method:POST
- AND querymethod:PUT OR method:POST
- OR query(method:PUT) AND (content:example OR content:test) AND NOT (content:foobar)
created_at:[\"2022-01-01 00:00:00\" TO \"2022-12-31 00:00:00\"]
- date range querycreated_at:[\"2022-01-01 00:00:00\" TO *]
- date range query (from date until now)created_at:[now-10m TO now]
- date range query (10 minutes ago until now; reference for date expressions)If you're in doubt about where these parameters go in an API request, take a look below. This URL combines a search query via the query
parameter (searching requests containing the word foobar
), as well as the sorting
and per_page
parameters.
https://webhook.site/token/a94a7294-c4aa-4074-ab77-c4cf86fd53b1/requests?query=content:foobar&sorting=newest&per_page=10
{\n\"data\": [\n{\n\"uuid\": \"a2a6a4ae-4130-4063-953a-84fa29d81d43\",\n\"token_id\": \"a94a7294-c4aa-4074-ab77-c4cf86fd53b1\",\n\"ip\": \"127.0.0.1\",\n\"hostname\": \"webhook.site\",\n\"method\": \"POST\",\n\"user_agent\": \"Paw\\/3.1.8 (Macintosh; OS X\\/10.14.6) GCDHTTPRequest\",\n\"content\": \"{\\\"first_name\\\":\\\"Arch\\\",\\\"last_name\\\":\\\"Weber\\\"}\",\n\"query\": {\n\"action\": \"create\"\n},\n\"request\": {\n\"status\": \"example\"\n},\n\"files\": {\n\"file\": {\n\"id\": \"98bf4c25-58ab-4c5d-ba91-fb6f709ea78d\",\n\"filename\": \"example.png\",\n\"size\": 420915,\n\"content_type\": \"image/png\"\n}\n},\n\"headers\": {\n\"content-length\": [\n\"271\"\n],\n\"user-agent\": [\n\"Paw\\/3.1.8 (Macintosh; OS X\\/10.14.6) GCDHTTPRequest\"\n],\n\"request-id\": [\n\"37856131\"\n]\n},\n\"url\": \"https:\\/\\/webhook.site\\/a94a7294-c4aa-4074-ab77-c4cf86fd53b1\\/201?\",\n\"created_at\": \"2019-10-03 19:06:35\",\n\"updated_at\": \"2019-10-03 19:06:35\",\n\"custom_action_output\": []\n}\n],\n\"total\": 1,\n\"per_page\": 50,\n\"current_page\": 1,\n\"is_last_page\": true,\n\"from\": 1,\n\"to\": 1\n}\n
"},{"location":"api/requests.html#get-singlelatest-request","title":"Get single/latest request","text":"GET /token/:token_id/request/:request_id
GET /token/:token_id/request/latest
- retrieves the latest request sent to the URL
{\n\"uuid\": \"a2a6a4ae-4130-4063-953a-84fa29d81d43\",\n\"token_id\": \"a94a7294-c4aa-4074-ab77-c4cf86fd53b1\",\n\"ip\": \"127.0.0.1\",\n\"hostname\": \"webhook.site\",\n\"method\": \"POST\",\n\"user_agent\": \"Paw\\/3.1.8 (Macintosh; OS X\\/10.14.6) GCDHTTPRequest\",\n\"content\": \"{\\\"first_name\\\":\\\"Arch\\\",\\\"last_name\\\":\\\"Weber\\\"}\",\n\"query\": {\n\"action\": \"create\"\n},\n\"headers\": {\n\"content-length\": [\n\"271\"\n],\n\"user-agent\": [\n\"Paw\\/3.1.8 (Macintosh; OS X\\/10.14.6) GCDHTTPRequest\"\n]\n},\n\"files\": {\n\"foo\": {\n\"id\": \"65d6e0ce-a840-47bc-b6b6-ff1ff38c34ca\",\n\"filename\": \"example.json\",\n\"size\": 5132873,\n\"content_type\": \"text/plain\"\n}\n},\n\"url\": \"https:\\/\\/webhook.site\\/a94a7294-c4aa-4074-ab77-c4cf86fd53b1\\/201?\",\n\"created_at\": \"2019-10-03 19:06:35\",\n\"updated_at\": \"2019-10-03 19:06:35\"\n}\n
"},{"location":"api/requests.html#get-raw-request-content","title":"Get raw request content","text":"GET /token/:token_id/request/:request_id/raw
GET /token/:token_id/request/latest/raw
- retrieves the latest request sent to the URL
Returns the request as a response (body, content-type.)
"},{"location":"api/requests.html#response_3","title":"Response","text":"200 OK
GET /token/:tokenId/request/:requestId/download/:fileId
Files that are included in a request or as email attachments are available to download using this endpoint.
"},{"location":"api/requests.html#response_4","title":"Response","text":"304 Redirect
DELETE /token/:token_id/request/:request_id
Deletes a request.
"},{"location":"api/requests.html#response_5","title":"Response","text":"200 OK
DELETE /token/:token_id/request
Deletes all requests associated with the token, or if query
, date_from
and/or date_to
is specified, only that subset of requests is deleted.
date_from
, date_to
- filter requests by date, format yyyy-MM-dd HH:mm:ss
query
- filter requests by a query string search. See here for examples.200 OK
More info about Schedules.
"},{"location":"api/schedules.html#create-schedule","title":"Create schedule","text":"POST /schedules
name
The name of the schedule.interval
One of the following interval strings: monthly
, weekly
, daily
, hourly
, 10-minute
, 5-minute
, 1-minute
, cron
cron
When interval
is cron
, specify a cron-style interval, e.g. */5 * * * *
for every 5 minutes. Otherwise can be left out.request_url
The request URL that the schedule should act on.request_method
HTTP Method (POST, GET, etc.)request_body
request_headers
HTTP headers, separated by \\n
timeout
Timeout in seconds (min 1, max 30)require_body
If specified, Webhook.site sends an error notification if response body doesn't contain this string.require_status_min
If specified, Webhook.site sends an error notification if response status doesn't fit within range.require_status_max
If specified, Webhook.site sends an error notification if response status doesn't fit within range.Variables will be replaced in the fields request_url
, request_method
, request_headers
and request_body
. More info here.
{\n\"name\": \"My schedule\",\n\"interval\": \"5-minute\",\n\"request_url\": \"https://example.com\",\n\"request_method\": \"POST\",\n\"request_body\": \"{\\\"json\\\": \\\"message\\\"}\",\n\"request_headers\": \"Authorization: Bearer mytoken\\nContent-Type: application/json\"\n}\n
"},{"location":"api/schedules.html#response","title":"Response","text":"{\n\"name\": \"My schedule\",\n\"interval\": \"5-minute\",\n\"request_url\": \"https:\\/\\/example.com\",\n\"request_method\": \"POST\",\n\"request_body\": \"{\\\"json\\\": \\\"message\\\"}\",\n\"request_headers\": \"Authorization: Bearer mytoken\\nContent-Type: application\\/json\",\n\"require_body\": null,\n\"require_status_min\": null,\n\"require_status_max\": null,\n\"user_id\": 21,\n\"updated_at\": \"2021-05-01 13:27:25\",\n\"created_at\": \"2021-05-01 13:27:25\",\n\"id\": 58\n}\n
"},{"location":"api/schedules.html#get-all-schedules","title":"Get all schedules","text":"GET /schedules?page=1&per_page=15
{\n\"current_page\": 1,\n\"data\": [\n{\n\"id\": 58,\n\"name\": \"My schedule\",\n\"interval\": \"5-minute\",\n\"cron\": null,\n\"user_id\": 21,\n\"request_method\": \"POST\",\n\"request_url\": \"https:\\/\\/example.com\",\n\"request_headers\": \"Authorization: Bearer mytoken\\nContent-Type: application\\/json\",\n\"request_body\": \"{\\\"json\\\": \\\"message\\\"}\",\n\"timeout\": 5,\n\"require_body\": null,\n\"require_status_min\": null,\n\"require_status_max\": null,\n\"last_run_at\": null,\n\"last_status\": null,\n\"created_at\": \"2021-05-01 13:27:25\",\n\"updated_at\": \"2021-05-01 13:27:25\"\n}\n],\n\"first_page_url\": \"https:\\/\\/webhook.site\\/schedules?page=1\",\n\"from\": 1,\n\"last_page\": 1,\n\"last_page_url\": \"https:\\/\\/webhook.site\\/schedules?page=1\",\n\"next_page_url\": null,\n\"path\": \"https:\\/\\/webhook.site\\/schedules\",\n\"per_page\": 15,\n\"prev_page_url\": null,\n\"to\": 6,\n\"total\": 6\n}\n
"},{"location":"api/schedules.html#get-single-schedule","title":"Get single schedule","text":""},{"location":"api/schedules.html#request_1","title":"Request","text":"GET /schedules/:scheduleId
{\n\"id\": 58,\n\"name\": \"My schedule\",\n\"interval\": \"5-minute\",\n\"cron\": null,\n\"user_id\": 21,\n\"request_method\": \"POST\",\n\"request_url\": \"https:\\/\\/example.com\",\n\"request_headers\": \"Authorization: Bearer mytoken\\nContent-Type: application\\/json\",\n\"request_body\": \"{\\\"json\\\": \\\"message\\\"}\",\n\"timeout\": 5,\n\"require_body\": null,\n\"require_status_min\": null,\n\"require_status_max\": null,\n\"last_run_at\": null,\n\"last_status\": null,\n\"created_at\": \"2021-05-01 13:27:25\",\n\"updated_at\": \"2021-05-01 13:27:25\"\n}\n
"},{"location":"api/schedules.html#update-schedule","title":"Update schedule","text":""},{"location":"api/schedules.html#request_2","title":"Request","text":"PUT /schedules/:scheduleId
(See Create schedule above for request.)
"},{"location":"api/schedules.html#response_3","title":"Response","text":"(See Get single schedule for response.)
"},{"location":"api/schedules.html#delete-schedule","title":"Delete schedule","text":""},{"location":"api/schedules.html#request_3","title":"Request","text":"DELETE /schedules/:scheduleId
204 No content
POST /schedules/:scheduleId/run-now
GET /schedules/:scheduleId/logs
Set Accept
header to application/json
.
Rate limit: 60 requests per minute.
"},{"location":"api/schedules.html#response_5","title":"Response","text":"{\n\"data\": [\n{\n\"id\": 2,\n\"schedule_id\": 1,\n\"response_status\": 200,\n\"response_headers\": {\n\"Age\": [\n\"447707\"\n],\n\"Cache-Control\": [\n\"max-age=604800\"\n],\n\"Content-Type\": [\n\"text\\/html; charset=UTF-8\"\n]\n},\n\"response_body\": \"<!doctype html>\\n<html>\\n<head>...\",\n\"error\": null,\n\"created_at\": \"2022-11-06 19:27:09\",\n\"updated_at\": \"2022-11-06 19:27:09\"\n},\n{\n\"id\": 3,\n\"schedule_id\": 1,\n\"response_status\": 200,\n\"response_headers\": {\n\"Age\": [\n\"404973\"\n],\n\"Cache-Control\": [\n\"max-age=604800\"\n],\n\"Content-Type\": [\n\"text\\/html; charset=UTF-8\"\n]\n},\n\"response_body\": \"<!doctype html>\\n<html>\\n<head>...\",\n\"error\": null,\n\"created_at\": \"2022-11-06 19:30:02\",\n\"updated_at\": \"2022-11-06 19:30:02\"\n}\n],\n\"current_page\": 1,\n\"first_page_url\": \"https:\\/\\/webhook.site\\/control-panel\\/schedules\\/1\\/logs?page=1\",\n\"from\": 1,\n\"last_page\": 1,\n\"last_page_url\": \"https:\\/\\/webhook.site\\/control-panel\\/schedules\\/1\\/logs?page=1\",\n\"next_page_url\": null,\n\"path\": \"https:\\/\\/webhook.site\\/control-panel\\/schedules\\/1\\/logs\",\n\"per_page\": 15,\n\"prev_page_url\": null,\n\"to\": 2,\n\"total\": 2\n}\n
"},{"location":"api/templates.html","title":"API Endpoints: Templates","text":"Webhook.site Templates allows you to create templates of actions, and re-use these templates with the Include Template Custom Action.
"},{"location":"api/templates.html#create-template","title":"Create Template","text":"POST /template
name
The name of the template.actions
An array of objects containing Custom Actions. All parameters of actions except disabled
can be used. More info herevariables
An array of name-value objects containing predefined variables. These variables are defined before the action runs, and are available to any subsequent actions after the template is included.{\n\"actions\": [\n{\n\"name\": null,\n\"type\": \"conditions\",\n\"queue\": false,\n\"delay\": 0,\n\"condition\": null,\n\"parameters\": {\n\"conditions\": [\n{\n\"input\": \"$date$\",\n\"operator\": \"eq\",\n\"value\": \"$post-news-last-date$\"\n}\n],\n\"mode\": \"all\",\n\"action\": \"stop\"\n},\n\"order\": 2\n},\n{\n\"name\": null,\n\"type\": \"twitter_tweet\",\n\"queue\": false,\n\"delay\": 0,\n\"condition\": null,\n\"parameters\": {\n\"provider_id\": \"501133\",\n\"tweet\": \"$tweet$\"\n},\n\"order\": 3\n},\n{\n\"name\": null,\n\"type\": \"store_global_variable\",\n\"queue\": false,\n\"delay\": 0,\n\"condition\": null,\n\"parameters\": {\n\"name\": \"post-news-last-date\",\n\"value\": \"$date$\"\n},\n\"order\": 4\n}\n],\n\"name\": \"My Template\",\n\"variables\": [\n{\n\"name\": \"example\",\n\"value\": \"hello world\"\n}\n],\n}\n
"},{"location":"api/templates.html#response","title":"Response","text":"{\n\"name\": \"My Template\",\n\"variables\": [\n{\n\"name\": \"example\",\n\"value\": \"hello world\"\n}\n],\n\"team_id\": 1,\n\"updated_at\": \"2024-02-20T10:01:29.000000Z\",\n\"created_at\": \"2024-02-20T10:01:29.000000Z\",\n\"id\": 21,\n\"actions\": [\n{\n\"condition\": null,\n\"delay\": 0,\n\"parameters\": {\n\"mode\": \"all\",\n\"conditions\": [\n{\n\"input\": \"$date$\",\n\"operator\": \"eq\",\n\"value\": \"$post-news-last-date$\"\n}\n],\n\"action\": \"stop\"\n},\n\"token_id\": \"f1bd342e-c98b-4ac4-bf3f-c35f1f338161\",\n\"type\": \"conditions\",\n\"order\": 2,\n\"name\": null,\n\"template_id\": 21,\n\"uuid\": \"9b615454-3c64-4da2-a2f7-d524797a8925\",\n\"updated_at\": \"2024-02-20T10:01:29.000000Z\",\n\"created_at\": \"2024-02-20T10:01:29.000000Z\"\n},\n{\n\"condition\": null,\n\"delay\": 0,\n\"parameters\": {\n\"tweet\": \"$tweet$\",\n\"provider_id\": \"501133\"\n},\n\"token_id\": \"f1bd342e-c98b-4ac4-bf3f-c35f1f338161\",\n\"type\": \"twitter_tweet\",\n\"order\": 3,\n\"name\": null,\n\"template_id\": 21,\n\"uuid\": \"9b615454-3ce7-4c63-bc8b-3412694ae633\",\n\"updated_at\": \"2024-02-20T10:01:29.000000Z\",\n\"created_at\": \"2024-02-20T10:01:29.000000Z\"\n},\n{\n\"condition\": null,\n\"delay\": 0,\n\"parameters\": {\n\"name\": \"post-news-last-date\",\n\"value\": \"$date$\"\n},\n\"token_id\": \"f1bd342e-c98b-4ac4-bf3f-c35f1f338161\",\n\"type\": \"store_global_variable\",\n\"order\": 4,\n\"name\": null,\n\"template_id\": 21,\n\"uuid\": \"9b615454-3d5f-4cd6-bd4b-94b55e6f6f2f\",\n\"updated_at\": \"2024-02-20T10:01:29.000000Z\",\n\"created_at\": \"2024-02-20T10:01:29.000000Z\"\n}\n]\n}\n
"},{"location":"api/templates.html#get-all-templates","title":"Get all Templates","text":"GET /templates
{\n\"current_page\": 1,\n\"data\": [\n{\n\"name\": \"My Template\",\n\"variables\": [\n{\n\"name\": \"example\",\n\"value\": \"hello world\"\n}\n],\n\"team_id\": 1,\n\"updated_at\": \"2024-02-20T10:01:29.000000Z\",\n\"created_at\": \"2024-02-20T10:01:29.000000Z\",\n\"id\": 21,\n\"actions\": [\n{\n\"condition\": null,\n\"delay\": 0,\n\"parameters\": {\n\"mode\": \"all\",\n\"conditions\": [\n{\n\"input\": \"$date$\",\n\"operator\": \"eq\",\n\"value\": \"$post-news-last-date$\"\n}\n],\n\"action\": \"stop\"\n},\n\"token_id\": \"f1bd342e-c98b-4ac4-bf3f-c35f1f338161\",\n\"type\": \"conditions\",\n\"order\": 2,\n\"name\": null,\n\"template_id\": 21,\n\"uuid\": \"9b615454-3c64-4da2-a2f7-d524797a8925\",\n\"updated_at\": \"2024-02-20T10:01:29.000000Z\",\n\"created_at\": \"2024-02-20T10:01:29.000000Z\"\n},\n{\n\"condition\": null,\n\"delay\": 0,\n\"parameters\": {\n\"tweet\": \"$tweet$\",\n\"provider_id\": \"501133\"\n},\n\"token_id\": \"f1bd342e-c98b-4ac4-bf3f-c35f1f338161\",\n\"type\": \"twitter_tweet\",\n\"order\": 3,\n\"name\": null,\n\"template_id\": 21,\n\"uuid\": \"9b615454-3ce7-4c63-bc8b-3412694ae633\",\n\"updated_at\": \"2024-02-20T10:01:29.000000Z\",\n\"created_at\": \"2024-02-20T10:01:29.000000Z\"\n},\n{\n\"condition\": null,\n\"delay\": 0,\n\"parameters\": {\n\"name\": \"post-news-last-date\",\n\"value\": \"$date$\"\n},\n\"token_id\": \"f1bd342e-c98b-4ac4-bf3f-c35f1f338161\",\n\"type\": \"store_global_variable\",\n\"order\": 4,\n\"name\": null,\n\"template_id\": 21,\n\"uuid\": \"9b615454-3d5f-4cd6-bd4b-94b55e6f6f2f\",\n\"updated_at\": \"2024-02-20T10:01:29.000000Z\",\n\"created_at\": \"2024-02-20T10:01:29.000000Z\"\n}\n]\n}\n],\n\"first_page_url\": \"https:\\/\\/webhook.site\\/templates?page=1\",\n\"from\": 1,\n\"last_page\": 1,\n\"last_page_url\": \"https:\\/\\/webhook.site\\/templates?page=1\",\n\"links\": [\n{\n\"url\": null,\n\"label\": \"« Previous\",\n\"active\": false\n},\n{\n\"url\": \"https:\\/\\/webhook.site\\/templates?page=1\",\n\"label\": \"1\",\n\"active\": true\n},\n{\n\"url\": null,\n\"label\": \"Next »\",\n\"active\": false\n}\n],\n\"next_page_url\": null,\n\"path\": \"https:\\/\\/webhook.site\\/templates\",\n\"per_page\": 15,\n\"prev_page_url\": null,\n\"to\": 1,\n\"total\": 1\n}\n
"},{"location":"api/templates.html#update-template","title":"Update Template","text":""},{"location":"api/templates.html#request_2","title":"Request","text":"PUT /templates/:templateId
(See Create Template above for request.)
"},{"location":"api/templates.html#response_2","title":"Response","text":"(See Create Template above for response.)
"},{"location":"api/templates.html#delete-template","title":"Delete Template","text":""},{"location":"api/templates.html#request_3","title":"Request","text":"DELETE /templates/:templateId
204 No content
A token is a container for incoming requests and emails, and corresponds to a Webhook.site URL or Email. A token ID is a 36 character UUID consisting of hexadecimal characters and dashes.
Simply, the token ID is the part after https://webhook.site/
in the URL, or before @email.webhook.site
in the email address.
POST /token
After creating a token, the URL at https://webhook.site/{token.uuid}
becomes accessible, and emails can be sent to {token.uuid}@email.webhook.site
.
default_*
parameters sets the response of the URL.timeout
(int) waits an amount of seconds before returning the response, max 30. Intended for testing timeouts, requests to tokens with timeouts are rate limited; a high timeout value will incur a lower rate limit.expiry
(int) amount of seconds until token auto-expiration. Max value (and default for non-upgraded URLs) is 604800 (one week). Intended for e.g. automated testing pipelines. Leave out or set to null
to disable.request_limit
(int) - limits the request history amount from 1 to 10000 (default)cors
(bool) set to true will add CORS headers to the request so browsers will send cross-domain requests to the URLalias
(string) allows setting the alias of the token.actions
(bool) specifies if Custom Actions are enabled and executed on every request/email (true), or disabled (false.)clone_from
(uuid string) specifies a token UUID (or alias) that will act as a template for the new token. When specified, settingssuch as default content, timeout, password as well as Custom Actions are copied to the new token.group_id
(int) specifies which group ID the token should be added to.{\n\"default_status\": 200,\n\"default_content\": \"Hello world!\",\n\"default_content_type\": \"text/html\",\n\"timeout\": 0,\n\"cors\": false,\n\"expiry\": 604800,\n\"alias\": \"my-webhook\",\n\"actions\": true\n}\n
"},{"location":"api/tokens.html#example-2-creating-with-python-3","title":"Example 2: Creating with Python 3","text":"Requires the requests
module, which can be installed using pip install requests
. You'll also need to replace the API key. Create an API key here.
import requests\njson = {\n\"default_status\": 200,\n\"default_content\": \"Hello world!\",\n\"default_content_type\": \"text/html\",\n}\nheaders = {\n\"api-key\": \"00000000-0000-0000-0000-000000000000\"\n}\nr = requests.post('https://webhook.site/token', json=json, headers=headers)\nprint('URL Created: https://webhook.site/' + r.json()['uuid'])\n
"},{"location":"api/tokens.html#response","title":"Response","text":"200 OK
{\n\"redirect\": false,\n\"alias\": null,\n\"timeout\": 0,\n\"premium\": true,\n\"uuid\": \"9981f9f4-657a-4ebf-be7c-1915bedd4775\",\n\"ip\": \"127.0.0.1\",\n\"user_agent\": \"Paw\\/3.1.8 (Macintosh; OS X\\/10.14.6) GCDHTTPRequest\",\n\"default_content\": \"Hello world!\",\n\"default_status\": 200,\n\"default_content_type\": \"text\\/plain\",\n\"premium_expires_at\": \"2019-10-22 10:52:20\",\n\"created_at\": \"2019-09-22 10:52:20\",\n\"updated_at\": \"2019-09-22 10:52:20\"\n\"expires_at\": \"2019-09-29 10:52:20\"\n}\n
"},{"location":"api/tokens.html#get-token-list","title":"Get token list","text":"GET /token
Returns a list of all Tokens associated with an account.
"},{"location":"api/tokens.html#query-string-parameters","title":"Query string parameters","text":"per_page
- amount of requests returned, defaults to 50 (max 100)page
- page number to retrieve (default 1)order_by
- which field to order tokens by (created_at
(default) or token_id
)order_direction
- order direction (asc
(default) or desc
){\n\"current_page\": 1,\n\"data\": [\n{\n\"uuid\": \"44fb1548-cd1f-4928-880c-cce094e5e179\",\n\"redirect\": false,\n\"alias\": null,\n\"actions\": true,\n\"cors\": false,\n\"expiry\": false,\n\"timeout\": 0,\n\"premium\": true,\n\"user_id\": null,\n\"password\": true,\n\"ip\": \"127.0.0.1\",\n\"user_agent\": \"Mozilla\\/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit\\/605.1.15 (KHTML, like Gecko) Version\\/14.0.3 Safari\\/605.1.15\",\n\"default_content\": \"\",\n\"default_status\": 200,\n\"default_content_type\": \"text\\/plain\",\n\"premium_expires_at\": null,\n\"created_at\": \"2021-08-11 18:34:44\",\n\"updated_at\": \"2021-08-11 18:34:44\",\n\"latest_request_id\": \"ea5f5920-0398-465c-8f9c-8074f0d805a4\",\n\"latest_request_at\": \"2021-08-12 19:56:50\",\n\"group_id\": null,\n\"requests\": 1\n},\n...\n],\n\"first_page_url\": \"https:\\/\\/webhook.site\\/token?page=1\",\n\"from\": 1,\n\"last_page\": 1,\n\"last_page_url\": \"https:\\/\\/webhook.site\\/token?page=1\",\n\"next_page_url\": null,\n\"path\": \"https:\\/\\/webhook.site\\/token\",\n\"per_page\": 50,\n\"prev_page_url\": null,\n\"to\": 2,\n\"total\": 2\n}\n
"},{"location":"api/tokens.html#get-token","title":"Get token","text":"GET /token/:token_id
See POST /token
PUT /token/:token_id
See POST /token
See POST /token
PUT /token/:token_id/password
Sets a password to view the requests of a token.
"},{"location":"api/tokens.html#request_3","title":"Request","text":"{\"password\": \"hunter2\", \"old_password\": \"hunter1\"}\n
"},{"location":"api/tokens.html#response_4","title":"Response","text":"See POST /token
PUT /token/:token_id/alias
Sets the alias for the token, which makes the token available at https://webhook.site/<alias>
or <alias>@email.webhook.site
in addition to its 36 character UUID.
Rules for alias format: Length between 3-32 characters. Allowed characters: A-Z, a-z and - (dash.)
"},{"location":"api/tokens.html#request_4","title":"Request","text":"{\"alias\": \"my-webhook\"}\n
"},{"location":"api/tokens.html#response_5","title":"Response","text":"See POST /token
Attaches CORS headers to the response of the Token, allowing browsers to request it from all domains.
PUT /token/:token_id/cors/toggle
{ \"enabled\": true}\n
"},{"location":"api/tokens.html#delete-token","title":"Delete token","text":"DELETE /token/:token_id
204 No Content
Executes custom scripts using a scripting language that's very similar to JavaScript and PHP. More information here
"},{"location":"custom-actions/action-types.html#text","title":"Text","text":""},{"location":"custom-actions/action-types.html#extract-jsonpath","title":"Extract JSONPath","text":"This action runs a JSONPath query on the contents of a request. With it, you can extract any data from a JSON document and store it in a variable, which can then be used in a downstream action.
JSONPath is very similar to the jq
commandline utility.
Example data:
{\n\"store\": {\n\"name\": \"Cool Books Ltd\",\n\"books\": [\n{\n\"title\": \"12 Rules for Life\",\n\"author\": \"Jordan B. Peterson\",\n\"author.age\": 60,\n\"price\": 10,\n\"isbn\": \"13123123123\"\n},\n{\n\"title\": \"How to Win Friends and Influence People\",\n\"author\": \"Dale Carnegie\",\n\"price\": 9,\n\"isbn\": \"23482394\"\n}\n]\n}\n}\n
JSONPath Result .store.name
name
property of store
object .store.books[0][\"author.age\"]
author age of first book (bracket syntax can be useful for e.g. keys containing periods) $.store.books[*].author
the authors of all books in the store $..author
all authors $.store..price
the price of everything in the store. $..books[2]
the third book $..books[(@.length-1)]
the last book in order. $..books[-1:]
the last book in order. $..books[0,1]
the first two books $..books[:2]
the first two books $..books[::2]
every second book starting from first one $..books[1:6:3]
every third book starting from 1 till 6 $..books[?(@.isbn)]
filter all books with isbn number property $..books[?(@.isbn != '')]
filter all books with isbn that isn't null or \"\"
(empty string.) (Bracket syntax can also be used here, e.g. .values[?(@['my value'] != '')]
) $..books[?(@.price<10)]
filter all books cheaper than 10 $..*
all elements in the data (recursively extracted)"},{"location":"custom-actions/action-types.html#jsonpath-syntax","title":"JSONPath Syntax","text":"Symbol Description $
The root object/element (not strictly necessary) @
The current object/element .
or []
Child operator ..
Recursive descent *
Wildcard. All child elements regardless their index. [,]
Array indices as a set [start:end:step]
Array slice operator borrowed from ES4/Python. ?()
Filters a result set by a script expression ()
Uses the result of a \"script\" expression as the index For more details on what's possible with JSONPath, take a look at the docs.
As you start entering a JSONPath, the results are validated and shown next to the input field.
"},{"location":"custom-actions/action-types.html#auto-json","title":"Auto JSON","text":"This action automatically converts JSON data to Webhook.site Variables, and can be used as an alternative for Extract JSONPath when there's a large amount of variables that need to be extracted.
Per default, the action works on the JSON found in the $request.content$
variable, e.g. the request body data.
If the JSONPath parameter is specified, this can be used to limit the variable creation to only the subset of data specified by the JSONPath query.
"},{"location":"custom-actions/action-types.html#auto-json-example","title":"Auto JSON Example","text":"If the following data is specified in the Source parameter:
{\n\"Actors\": [\n{\n\"name\": \"Tom Cruise\",\n\"age\": 56,\n\"Born At\": \"Syracuse, NY\",\n\"Birthdate\": \"July 3, 1962\",\n\"photo\": \"https://jsonformatter.org/img/tom-cruise.jpg\"\n},\n{\n\"name\": \"Robert Downey Jr.\",\n\"age\": 53,\n\"Born At\": \"New York City, NY\",\n\"Birthdate\": \"April 4, 1965\",\n\"photo\": \"https://jsonformatter.org/img/Robert-Downey-Jr.jpg\"\n}\n]\n}\n
If the JSONPath parameter is empty, the following 10 variables will be created:
Variable Name Value$json.Actors.0.name$
Tom Cruise $json.Actors.0.age$
56 $json.Actors.0.Born At$
Syracuse, NY $json.Actors.0.Birthdate$
July 3, 1962 $json.Actors.0.photo$
https://example.com/tom-cruise.jpg $json.Actors.1.name$
Robert Downey Jr. $json.Actors.1.age$
53 $json.Actors.1.Born At$
New York City, NY $json.Actors.1.Birthdate$
April 4, 1965 $json.Actors.1.photo$
https://example.com/Robert-Downey-Jr.jpg If the JSONPath parameter is set to .Actors.0
, only the following 5 variables are created:
$json.0.name$
Tom Cruise $json.0.age$
56 $json.0.Born At$
Syracuse, NY $json.0.Birthdate$
July 3, 1962 $json.0.photo$
https://example.com/tom-cruise.jpg"},{"location":"custom-actions/action-types.html#extract-regex","title":"Extract Regex","text":"This action runs a Regex (regular expression) query on the contents of a request. With it, you can extract any data from a text document and store it in a variable, which can then be used in a downstream action.
As you start entering a Regex, the results are validated and shown next to the input field.
"},{"location":"custom-actions/action-types.html#extract-xpath","title":"Extract XPath","text":"Similar to the Extract JSONPath Custom Action, Extract XPath lets you extract values from an XML or HTML document and save the result as a variable.
"},{"location":"custom-actions/action-types.html#xpath-examples","title":"XPath Examples","text":"The following examples are based on this XML document:
<?xml version=\"1.0\"?>\n<organization name=\"ExampleCo\">\n<employees>\n<employee id=\"1\">Jack</employee>\n<employee id=\"2\">Ann</employee>\n</employees>\n</organization>\n
Example XPath Notes Result /organization
Finds all content within the organization element JackAnn //employee[@id != 1]
//
traverses all <employee>
elements in document, the @id query selects all except those with id
=1 Jack /organization/@name
@name
to get the \"name\" property of the element ExampleCo /organization/employees/employee[2]
[2]
specifies 2nd element Ann /organization/employees/employee[2]/@id
Get the \"id\" property of second employee element 2 /organization/employees/employee[@id=1]
Employee element with id property equal to \"1\" Jack /organization/employees/employee[last()]
Last employee element Ann //employee[contains(@id, \"2\")]
Employee within any parent element where id contains \"2\" Ann For more examples, see W3CSchools or XPath Cheatsheet
"},{"location":"custom-actions/action-types.html#replace-text","title":"Replace Text","text":"An action that allows replacing multiple inputs to a string with specified replacements. Additionally, Webhook.site will replace all variables in the source text as well as the text being replaced, and the replacement.
"},{"location":"custom-actions/action-types.html#split-text","title":"Split Text","text":"Split text into multiple variables. Using hello,world
as Source, and ,
as Delimiter, 2 variables will be created: $variable.1$
is \"hello\" and $variable.2$
is \"world\".
Sets a variable depending on what maps to the source value and operator.
If we set Source to John
, Operator to ends with
, Variable Name to $user_id$
, Default to unknown
, and add a mapping of From: John
-> To: 123
, then the variable name $user_id$
would be set to 123
.
If the Source had been Jack
, $user_id$
would have been set to unknown.
This will send a HTTP/HTTPS request from the Webhook.site cloud.
The HTTP Request action has several modes:
The response of the request is stored in a series of variable names prefixed with a value of your choosing. The following variables are set after the request has been fired:
$your_prefix.content$
- response body content$your_prefix.status$
- response status code$your_prefix.headers$
- response headers$your_prefix.url$
- the URL the request was sent to$your_prefix.error$
- if the request resulted in an error, it's stored in this variable.This will send a email with variable contents from the Webhook.site cloud. Variables extracted previously can be used.
"},{"location":"custom-actions/action-types.html#send-email-smtp","title":"Send Email (SMTP)","text":"This will send a email with variable contents from your own email provider. Variables extracted previously can be used.
"},{"location":"custom-actions/action-types.html#gmail-and-google-workspace","title":"Gmail and Google Workspace","text":"For Gmail, the following specific setup is required:
smtp.gmail.com
587
TLS
Allows you to run one or more SSH command on a server. Webhook.site captures the output (stdout), stderr and the command exit code as Variables that can be used in downstream actions:
$ssh.stdout$
$ssh.stderr$
$ssh.exit$
We recommend authenticating using a pre-generated keypair, which can be created under Control Panel -> Providers.
"},{"location":"custom-actions/action-types.html#sftp-upload","title":"SFTP Upload","text":"Allows uploading a file to a SFTP (SSH) server, specifying a hostname, port, username, password, relative path to the file. The file content can be specified, in which Variables are replaced.
We recommend authenticating using a pre-generated keypair, which can be created under Control Panel -> Providers.
"},{"location":"custom-actions/action-types.html#ftps-upload","title":"FTP(S) Upload","text":"Allows uploading a file to a FTP or FTPS (FTP with TLS/SSL) server, specifying a hostname, port, username, password, relative path to the file, whether to use SSL and whether to use passive mode. Finally, the file content can be specified, in which Variables are replaced.
We recommend storing the password as a Global Variable.
"},{"location":"custom-actions/action-types.html#database-query","title":"Database Query","text":"Allows running a database query, with support for fetching out data in a series of variables. We recommend storing the password as a Global Variable.
"},{"location":"custom-actions/action-types.html#supported-database-servers","title":"Supported Database Servers","text":"Currently supported are:
If your database server is not on the list, please contact support.
"},{"location":"custom-actions/action-types.html#using-parameters","title":"Using Parameters","text":"When using e.g. INSERT or UPDATE statements, we strongly recommend using parameters for each column value. Doing this, you avoid SQL injection attacks and other issues when using user-submitted data (e.g. via Variables), or even just data containing special characters like quotes, that could otherwise break a query.
Each parameter name should start with a colon (:) and be a single word. You can then reference these parameters inside the query, like in the following example:
"},{"location":"custom-actions/action-types.html#fetching-data","title":"Fetching data","text":"When fetching data using e.g. SELECT statements, Webhook.site automatically inserts data in a series of Custom Action Variables, which are then available to downstream actions.
For example, when fetching rows from the following table:
Using the following statement:
select * from employees\n
If the variable name prefix would be set to output
, the following variables would be created containing specific values:
Additionally, a variable would be created with the name $output.json$
containing the data in JSON format:
[ {\n\"id\": 1,\n\"fname\": \"Simon\",\n\"lname\": \"Fredsted\",\n\"title\": \"Founder\"\n},\n{\n\"id\": 2,\n\"fname\": \"Jack\",\n\"lname\": \"Daniels\",\n\"title\": \"Assistant\"\n}\n]\n
"},{"location":"custom-actions/action-types.html#behavior","title":"Behavior","text":""},{"location":"custom-actions/action-types.html#dont-save","title":"Don't Save","text":"Marks the request so it is not saved in Webhook.site, which is useful (especially in combination with Conditions) when receiving a large amount of requests.
"},{"location":"custom-actions/action-types.html#log","title":"Log","text":"Adds a custom log entry to the Request's action output.
"},{"location":"custom-actions/action-types.html#modify-response","title":"Modify Response","text":"This action can be used to modify the response of the Webhook.site URL based on the input.
"},{"location":"custom-actions/action-types.html#rate-limit","title":"Rate Limit","text":"This action can be used to allow a specific amount of requests in a specific amount of time per a given IP.
If the IP is rate limited, the URL will respond with a HTTP 429
, action execution is stopped, and the request is not saved in Webhook.site.
Immediately stops Custom Action execution and returns the default response.
"},{"location":"custom-actions/action-types.html#logic","title":"Logic","text":""},{"location":"custom-actions/action-types.html#conditions","title":"Conditions","text":"Useful if you need to validate that the request does or does not conform to certain criteria, the Condition action will either stop or continue based on a condition.
In both the input and the value fields, variables will be replaced (including Global Variables from the Control Panel), so you can compare e.g. JSONPath or Regex values - or even values from a previous HTTP request that was sent.
Currently, three actions are provided: use result, stop and continue. Use Result allows using the Condition result in further actions. Stop will stop further action execution of the condition is a match. Continue will only continue further execution if the condition is a match, and otherwise stop.
The following \"operators\" are available:
The \"result\" of the condition will be logged below the request details, so you can see what happened.
To use the result of the Condition, select it in the \"only run when condition passes\" checkbox:
Tip: To check if a Variable is set (or exists), you must enter the variable name in both input and value fields and use the \"is not equal to\" operator, since non-existing variables are not replaced.
"},{"location":"custom-actions/action-types.html#javascript-beta","title":"JavaScript (Beta)","text":"With the JavaScript action, you can execute most kinds of JavaScript/Node.js code using a Node.js 20.10.0 sandbox.
"},{"location":"custom-actions/action-types.html#limitations","title":"Limitations","text":"This feature is still in beta as of late 2023, and we expect to add more features in the coming months.
Currently identified limitations include:
console.log(line)
/ echo(line)
- log a string to Action output
set(variable_name, line)
- sets a Webhook.site variable for use in downstream actions
The following code would set the variable $myvar$ to value
:
set('myvar', 'value')\n
get(variable_name, line)
- gets a Webhook.site variable (except Global Variables; see below)
variables[]
- global array variable containing Webhook.site variables
stop()
- stops action execution and return response
dont_save()
- marks current requests as Don't Save, so it won't be stored or shown in the Webhook.site requests list
respond(content, status, headers)
- stops action execution and return response
respond('OK', 200, ['Content-Type: text/plain'])\n
set_response(content, status, headers)
- sets response, but doesn't stop action execution
Code executed with the Webhook.site JavaScript action runs in a sandbox where the following utility libraries are available by using the require()
function:
axios
- HTTP Client axios = require('axios')\nawait axios.get('https://webhook.site')\n.then(response => {\nconsole.log(response.status)\n});\n// 200\n
lodash
- General utility library _ = require('lodash')\nconsole.log(_.last([1, 2, 3]))\n// 3\n
dayjs
- Date and time manipulation dayjs = require('dayjs')\nconsole.log(dayjs(1318781876406))\n// \"2011-10-16T16:17:56.406Z\"\n
cheerio
- JQuery-like HTML selector library cheerio = require('cheerio')\nconst $ = cheerio.load('<ul id=\"fruits\">banana</ul>');\nconsole.log($('#fruits').text());\n// banana\n
jsonpath
- JSONPath query library jsonpath = require('jsonpath')\nvar cities = [\n{ name: \"London\", \"population\": 8615246 },\n{ name: \"Berlin\", \"population\": 3517424 },\n];\nvar names = jsonpath.query(cities, '$..name');\nconsole.log(names)\n// [\"London\",\"Berlin\"]\n
crypto
\u2013 Node.js built-in crypto library crypto = require('crypto');\nconst secret = 'abcdefg';\nconst hash = crypto.createHmac('sha256', secret)\n.update('I love Webhook.site')\n.digest('hex');\nconsole.log(hash);\n// 1745c764246c2782ebf97e25b89547a92571f19d41846b42880d3815480f098e\n
faker
- Seed data generator faker = require('faker')\nconsole.log(faker.internet.email())\n// Ila_Gutkowski9@yahoo.com\n
Do you need a library that isn't listed here? Please contact support!
"},{"location":"custom-actions/action-types.html#webhookscript_1","title":"WebhookScript","text":"For more information about WebhookScript, see the dedicated page.
"},{"location":"custom-actions/action-types.html#set-variable","title":"Set Variable","text":"Defines (or overwrites) a variable that's available to downstream actions. The variable is not saved permanently as a Global Variable.
There are three modes:
Saves (or overwrites) a Global Variable that's saved permanently and available to all URLs in your account. If you don't need to save the variable permanently, you should use the Set Runtime Variable instead.
"},{"location":"custom-actions/action-types.html#image-handling","title":"Image Handling","text":""},{"location":"custom-actions/action-types.html#resize-image","title":"Resize Image","text":"Takes an image from either a URL or raw image data from e.g. a file upload, email attachment, request response or another action such as Dropbox.
You can enter both width and height to contrain the image in both dimensions, or enter a single dimension.
Check \"Keep Aspect Ratio\" so that the image keeps the aspect ratio, but doesn't exceed the height and width constraints.
"},{"location":"custom-actions/action-types.html#google-sheets","title":"Google Sheets","text":"Note
Google Sheets should not be used as a database, and have low usage limits. If you need to import on the order of thousands of rows or make thousands of calls a day, Google Sheets cannot be used. We recommend using a database like Postgres in conjunction with the Database Query action.
Google Sheets Custom Actions lets you manipulate and retrieve values from a Google Sheet.
The following Google Sheets Custom Actions are available:
To start, you need to make sure that you have connected a Google account in the Control Panel, available here.
After that, you can select the account in the dropdown when creating the Custom Action.
"},{"location":"custom-actions/action-types.html#usage-limits","title":"Usage Limits","text":"It is important to note that Google will block Write requests (i.e. adding or updating rows) at 60 requests per minute. After that, the action will temporarily fail with the following error message:
Quota exceeded for quota metric 'Write requests' and limit 'Write requests per minute per user' of service 'sheets.googleapis.com' for consumer\n
Therefore, for importing mass amounts of data in a short timespan, Google Sheets is not recommended. Instead, we recommend using the Database Query action.
Additionally, Webhook.site will automatically disable Google Sheets actions that continously fail due to e.g. quota errors.
"},{"location":"custom-actions/action-types.html#specifying-the-spreadsheet","title":"Specifying the spreadsheet","text":"When specifying the spreadsheet, you can either just copy/paste the spreadsheet URL or enter the spreadsheet ID. Variables can be used to specify the spreadsheet.
"},{"location":"custom-actions/action-types.html#ranges","title":"Ranges","text":"All actions must specify a range, which behaves similar in all actions. For the Add Row action, Google Sheets will automatically find a \"table\" (e.g. a homogenous mass of data) and add the values at the bottom.
A range is the same query as in Google Sheets, e.g. to select A1-C3 in Worksheet \"Example\", enter 'Example'!A1:C3
.
When inserting or updating values, you can either enter a value in the text field, or supply multiple cells and/or rows using JSON. To insert two rows, the JSON would be [\"cell 1\", \"cell 2\"]
.
The Get Values Action allows you to define variables based on the output. Since this action can return multiple pieces of data, multiple variables are created.
For example, if you select two columns and two rows, e.g. A1:B2
, four variables would be defined:
variable_name.0.0
= value of A1variable_name.0.1
= value of A2variable_name.1.0
= value of B1variable_name.1.1
= value of B2Additionally, the data is available in JSON, with the variable_name.json
variable being defined, and continuing with the example above, would contain the following JSON:
[\n[\"A1\",\"A2\"],\n[\"B1\",\"B2\"]\n]\n
"},{"location":"custom-actions/action-types.html#microsoft","title":"Microsoft","text":""},{"location":"custom-actions/action-types.html#onedrive","title":"OneDrive","text":"With Webhook.site, you can now use your Microsoft account to upload and download files in your OneDrive account using the Upload and Download Custom Actions.
"},{"location":"custom-actions/action-types.html#excel","title":"Excel","text":"Using the Add Rows and Get Values actions, Webhook.site now allows using your Microsoft account to append and retrieve data from Excel worksheets in your OneDrive account.
"},{"location":"custom-actions/action-types.html#amazon-web-services-aws","title":"Amazon Web Services (AWS)","text":""},{"location":"custom-actions/action-types.html#s3","title":"S3","text":"The following actions are available for AWS S3:
In addition to the \"official\" Amazon endpoints, Webhook.site also supports S3-compatible storages like DigitalOcean, MinIO, Wasabi and more. The endpoint can be specified when setting up the account in Control Panel.
"},{"location":"custom-actions/action-types.html#cloudfront","title":"CloudFront","text":"The \"Create Invalidation\" action allows you to dynamically create a CloudFront cache invalidation as a Custom Action. Both the Distribution ID and the paths to be invalidated are replaced with Webhook.site Variables.
"},{"location":"custom-actions/action-types.html#discord","title":"Discord","text":"With the Discord Custom Action, you can send messages to a specified channel (Each bot account uses a specific channel, so you can connect more accounts to send to different channels or servers.) In addition, you can choose a custom username and avatar image for the bot user.
"},{"location":"custom-actions/action-types.html#slack","title":"Slack","text":"With the Slack Custom Action, you can easily use Slack's Webhook URLs to send messages to a channel.
"},{"location":"custom-actions/action-types.html#dropbox","title":"Dropbox","text":"The Dropbox integration has access to the entire contents of your dropbox, and currently the following actions are available:
The X Integration supports the following actions using X's API:
The RabbitMQ Integration allows you to publish and consume messages from a RabbitMQ queue by specifying the server connection details.
"},{"location":"custom-actions/action-types.html#pushed","title":"Pushed","text":"With the Send Push Notification action, you can easily send push notifications to your mobile devices using your Pushed.co account.
With a free Pushed.co account, you can send up to 1000 push notifications a month.
"},{"location":"custom-actions/action-types.html#ntfysh","title":"ntfy.sh","text":"Allows you to easily send push notifications to your browser, phone, watch, etc. Simply download the ntfy.sh app, subscribe to your topic name and send a message to the topic name via this Custom Action. No account required.
App download links:
While Webhook.site doesn't have a native integration with Airtable, due to the simple API, it's exceedingly easy to add rows to a so-called Airtable Base.
First, create an API key on your Account page: https://airtable.com/account.
Next, go to Airtable's API documentation and select the Base you want to interact with: https://airtable.com/api. In the upper right corner, make sure show API key is checked.
If you then scroll down to the Create records section, you can essentially copy everything over to a Send Request action.
Things to note:
POST
--data '
and the final '
(quote) characterVariables are the most important part of Custom Actions, and are characterized by a name surrounded by two dollar signs: $example$
. Variables can be used in any field that has a \u24e5 icon in the editor. They act as placeholders that are replaced by dynamic content as the request or email is received.
Each request or email has a set of Base Variables (see below) that contain information like the request IP, method, headers, query string values, form values and the request content. To see a list of variables, click the Variables button in the editor. Clicking on a variable copies it to the clipboard.
Many of the the available Custom Actions can register a variable during the runtime of the actions, so for example you can register the result of a JSONPath query and use it in a \"Modify Response\" action to make the response dynamic, or even use it to send a request to another HTTP address, and then use the response of that. Files can referred to and be used through Variables.
This works since Custom Actions are executed synchronously in a chain, sharing data as they're being executed.
The format of variables are dollar signs surrounded by a word, for example: $example$
.
In Webhook.site Control Panel, you can define Global Variables which can be accessed between all URLs and used in Schedules. Global Variables are permanent.
Global Variables can also be created, modified or deleted using Custom Actions, including in WebhookScript and JavaScript actions.
Additionally, Global Variables can be managed using the Webhook.site API.
"},{"location":"custom-actions/variables.html#variable-modifiers","title":"Variable Modifiers","text":"Adding specific suffixes to variable names will let you process the value in the following ways:
Variable Example Input Example Output Description $example${\"json\": \"value\"}
{\"json\": \"value\"}
no modifier $example.json$ {\"json\": \"value\"}
{\\\"json\\\": \\\"value\\\"}
Escapes all special JSON characters, allowing to use any string in a JSON object. Escaped characters include \\b, \\f, \\n, \\r, \\t, \", \\ $example.html_encode$ <p>some html</p>
<p>some html</p>
Escapes all special HTML characters $example.html_decode$ <p>some html</p>
<p>some html</p>
Replaces all escaped HTML escapes with normal characters $example.html_strip$ <p>some html</p>
some html
Removes all HTML tags from input string $example.base64_encode$ {\"json\": \"<b>value</b>\"}
eyJqc29uIjogIjxiPnZhbHVlPC9iPiJ9Cg==
Encodes the variable to base64 $example.base64_decode$ eyJqc29uIjogIjxiPnZhbHVlPC9iPiJ9Cg==
{\"json\": \"<b>value</b>\"}
Decodes a base64 encoded string $example.url_encode$ {\"json\": \"value\"}
%7B%22json%22%3A+%22value%22%7D
Escapes all special HTTP URL characters $example.url_decode$ %7B%22json%22%3A+%22value%22%7D
{\"json\": \"value\"}
Replaces all special HTTP URL escapes with normal characters"},{"location":"custom-actions/variables.html#base-variables","title":"Base Variables","text":"These variables are automatically available for each request or email. Different variables are available depending on the type.
Variable Name Available For Description request.uuid All The UUID of the request request.token_id All The Token UUID (URL ID) of the request request.content All The body content of the request request.date All Creation date in Y-m-d H:m:s format request.date All Creation date in UNIX timestamp format request.hostname All Hostname of the request (usuallywebhook.site
) request.header.[name] All Created for each HTTP header request.size All Request body size in bytes request.type All Request type (email
or web
) request.file.[name].filename All Created for each file upload, with name
being the input name property. Contains the client file name request.file.[name].size All Contains the file size in bytes request.file.[name].content All Contains the file content request.file.[name].content_type All Contains the file content type (e.g. image/png) request.file.[name].id All Contains the Webhook.site file ID request.file.[name].link All Contains the direct download link to the file from Webhook.site's server. request.query.[name] Web Created for each query string (e.g. ?name=value) request.form.[name] Web Created for each form field request.ip Web IP of the host making the request request.user_agent Web User agent header request.url Web Full URL of the request (e.g. https://webhook.site/xxx-xxx...) request.path Web Sub-path of URL, after the token ID. Defaults to /
request.query Web Full query string of the URL request.method Web HTTP method (GET, POST, etc.) request.sender Email Sender address request.message_id Email Email message ID request.text_content Email Parsed plaintext content request.destinations Email Comma separated list of recipients. request.checks.[name] Email True or false for email checks (DKIM, SPF, etc.)"},{"location":"webhookscript/index.html","title":"About WebhookScript","text":"WebhookScript is an easy to use scripting language designed for executing Web-related actions on incoming requests.
While the other actions like Extract Regex and Send Email allows you to create flows in a visual editor, WebhookScript makes it quicker to create more advanced logic.
WebhookScript can be combined with other Custom Actions as data can be shared between them using Variables.
"},{"location":"webhookscript/index.html#syntax","title":"Syntax","text":"The syntax is very similar to PHP and JavaScript. See also the full language specification.
products = [\n'apple': ['price': 10],\n'blueberry': ['price': 1],\n'cake': ['price': 550]\n]\nshouldAddVat = var('request.query.vat');\nselectedProduct = var('request.query.product');\nif (!selectedProduct) {\nrespond('Please select a product!', 500)\n}\nprice = products[selectedProduct]['price'];\nif (shouldAddVat == 1) {\nprice = price * 1.25;\n}\nrespond(\n'Your price is {}'.format(price),\n200\n);\n
"},{"location":"webhookscript/index.html#variables-in-webhookscript","title":"Variables in WebhookScript","text":"Custom Action Variables in WebhookScript behave a little differently than other action types: in the code, they will not be replaced automatically like in other action types.
Instead, to interface with Custom Action Variables (created in previous actions, or default variables provided for each request or email), the function var() can be used.
The dollar-sign syntax (e.g. $request.content$
) is optional when using the var()
function, and the following two statements are equivalent: var('$request.content$')
/ var('request.content')
.
In addition, set() can be used to export a variable from your script to further downstream actions. store() is used to permanently set a Global Variable.
"},{"location":"webhookscript/index.html#about-the-editor","title":"About the Editor","text":""},{"location":"webhookscript/index.html#shortcuts","title":"Shortcuts","text":"The shortcuts are available when the editor is focused.
Windows Mac Shortcut Alt-R Alt-R Test code (update Debug Panel) Ctrl-S Cmd-S Save action without exiting"},{"location":"webhookscript/index.html#debug-panel","title":"Debug Panel","text":"Below the editor is the \"debug panel\" containing data relating to the current and previous actions:
Debug outputs shows the outputs of all the actions, with the current action being edited or created marked in blue.
Response shows details of the response of the URL formatted in JSON.
Variables is a table of all current available variables for use in the script with the var()
function or variables
array.
To enable fullscreen mode, click the Expand button in the upper right corner to make the editor take up more screen space. Click again to disengage fullscreen mode.
"},{"location":"webhookscript/date-format.html","title":"Date format characters","text":"WebhookScript uses the ISO format for converting and formatting dates, and the format is compatible with the Moment.js format method.
The following examples are based on the date 2017-01-05 17:04:05.084512
.
Source: Carbon Docs
"},{"location":"webhookscript/date-format.html#timezone-list","title":"Timezone List","text":"Info
Do you have a nice example to share with other users? Or looking for even more examples? Take a look at the WebhookScript example repository, and make a pull request if you want to contribute: Webhook.site Script Repository
"},{"location":"webhookscript/examples.html#hmac-validation","title":"HMAC validation","text":"The following script uses the hmac() function and the sha256
algorithm to verify a HMAC signature, which is commonly used to verify webhooks. In this example, the secret is secret
and the signature comes from the incoming request's x-signature
HTTP header.
If the signature doesn't match, further action execution is stopped and the URL immediately respond with a 401 status code.
hmac = hmac(var('request.content'), 'sha256', 'secret');\nsignature = var('request.header.x-signature', '');\nif (hmac != signature) {\nrespond('Unauthorized', 401);\n}\n
"},{"location":"webhookscript/examples.html#jwt","title":"JWT","text":"An example acquiring an access token from the DocuSign API using JWT and the WebhookScript sign()
function:
private_key = '-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBA...\n-----END RSA PRIVATE KEY-----';\nheader = json_encode([\n'alg': 'RS256',\n'typ': 'JWT'\n])\ncurrentTimestamp = now().date_format('X').to_number()\nbody = json_encode([\n\"iss\": \"00000000-0000-0000-0000-000000000000\",\n\"sub\": \"00000000-0000-0000-0000-000000000000\",\n\"aud\": \"account-d.docusign.com\",\n\"iat\": currentTimestamp,\n\"exp\": currentTimestamp + 6000,\n\"scope\": \"impersonation signature\"\n])\ntoken = base64url_encode(header) + \".\" + base64url_encode(body)\nsignature = base64url_encode(sign(token, private_key, 'sha256'))\njwt = token + \".\" + signature\ncontent = query([\n'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n'assertion': jwt\n])\nheaders = ['Content-Type: application/x-www-form-urlencoded']\nresponse = request('https://account-d.docusign.com/oauth/token', content, 'POST', headers)\nif (response['status'] != 200) {\necho('Could not get token: ' + response['content'])\nstop()\n}\nresponse_json = json_decode(response['content'])\necho('Got access token: ' + response_json['access_token'])\n
"},{"location":"webhookscript/examples.html#convert-a-date","title":"Convert a date","text":"In this example, if we assume the variable $mydate$
is set to 2021-07-26T16:23:50+03:00
, the variable will be overwritten to 2021.07.26 16:23
for actions running after the WebhookScript action.
input = var('$mydate$')\noutput = date_format(input, 'YYYY.MM.DD HH:mm')\nset('$mydate$', output)\n
More information about available date format characters.
"},{"location":"webhookscript/examples.html#request-counter","title":"Request counter","text":"This script uses Global Variables to keep track of the amount of requests sent to a Webhook.site URL within the last 10 minutes.
Once the 10 minutes are up, it sends a request to another endpoint with the amount of requests and the date, and then it resets the counter.
period = 600 // 10 minutes in seconds\ncurrentPeriod = var('current_period', now())\ncounter = var('counter', 0).to_number()\ncurrentPeriodSecs = date_interval(currentPeriod)\nif (currentPeriodSecs < period) {\ncounter = counter + 1\nstore('counter', counter)\n} else {\n// Above 10 minutes, reset and send request\nstore('counter', 0)\nstore('current_period', now())\nrequest(\n'https://webhook.site/14fea227-60ab-4291-b679-12b8c104c78e', json_encode([\n\"counter\": counter,\n\"timestamp\": now(),\n]),\n'POST'\n)\n}\necho('count : {}'.format(counter))\necho('period: {}'.format(currentPeriodSecs))\n
"},{"location":"webhookscript/examples.html#parse-and-loop-through-json","title":"Parse and loop through JSON","text":"This example shows various ways of iterating through JSON arrays, comparing data and rearranging it to another format.
First, it loops through the items
array, verifying that the item
must have a group ID that a specific one from an array.
Second, it changes the fields
array from an array of objects to a key-value object using the name
and value
fields.
// Define input as a JSON string\njson = '{\n \"items\": [\n {\n \"first_name\": \"Jack\",\n \"last_name\": \"Daniels\",\n \"phone\": \"+1 100-555-999\",\n \"group_ids\": [346, 46456, 23423],\n \"fields\": [\n {\n \"id\": 45698,\n \"name\": \"birthday\",\n \"value\": \"1990-01-01\"\n },\n {\n \"id\": 344,\n \"name\": \"car\",\n \"value\": \"BMW M2\"\n }\n ]\n },\n {\n \"first_name\": \"Jim\",\n \"last_name\": \"Beam\",\n \"phone\": \"+1 123-555-788\",\n \"group_ids\": [3456, 43546, 234234, 456456],\n \"fields\": [\n {\n \"id\": 45698,\n \"name\": \"birthday\",\n \"value\": \"1987-05-01\"\n },\n {\n \"id\": 344,\n \"name\": \"car\",\n \"value\": \"Toyota Corolla\"\n },\n {\n \"id\": 248,\n \"name\": \"nickname\",\n \"value\": \"Jimmie\"\n }\n ]\n }\n ]\n}'\n// Decode to array\ndata = json_decode(json)\n// Define an array of valid groups\nvalid_groups = [43546, 999999]\n// Loop over items\nfor (item in data['items']) {\n// Per default, item does not have a valid group.\nhas_valid_group = false\n// Loop through each group ID in the data\nfor (group_id in item['group_ids']) {\n// Does the \"valid_groups\" array contain this group ID?\nif (valid_groups.contains(group_id)) {\nhas_valid_group = true\n}\n}\nif (has_valid_group) {\necho(item['first_name'] + ' did not have a valid group.') } else {\necho(item['first_name'] + ' has a valid group.') }\n// Extract \"fields\" into an array of key => value\nfields = []\nfor (field in item['fields']) {\nfields[field['name']] = field['value']\n}\necho(json_encode(fields))\n}\n
Returns the following output:
Jack has a valid group.\n{\n \"birthday\": \"1990-01-01\",\n \"car\": \"BMW M2\"\n}\nJim did not have a valid group.\n{\n \"birthday\": \"1987-05-01\",\n \"car\": \"Toyota Corolla\",\n \"nickname\": \"Jimmie\"\n}\n
"},{"location":"webhookscript/examples.html#loop-through-and-compare-items","title":"Loop through and compare items","text":"In this example, we loop through a series of items and pick the item that's contained in a string.
location = 'test ABC example';\ncompares = [\n'123': 'token1',\n'ABC': 'token2',\n'DEF': 'token3',\n]\ntoken = ''; // Default value\nfor (compare in array_keys(compares)) {\nif (location.contains(compare)) {\ntoken = compares[compare]\n}\n}\ndump(token) // token2\n
"},{"location":"webhookscript/examples.html#submit-request-with-escaped-json","title":"Submit request with escaped JSON","text":"If you're building a JSON object, we recommend doing it in WebhookScript instead of typing JSON in the Send Request action type (If you do anyway, we recommend using the .json
Variable Modifier, More info here).
In this example, one of the JSON values contain HTML generated using the string_format function.
html_template = '<b><u>New {} lead</u></b><br>\n<br>\nLocation: {}<br>\nMessage from customer:<br>\n<div style=\"background:#CCC\">{}</div>'\nhtml_message = string_format(\nhtml_template,\nvar('lead_type'),\nvar('location'),\nvar('message')\n)\npayload = json_encode([\n'lead': [\n'firstname': var('firstname'),\n'lastname': var('lastname'),\n'html': html_message\n]\n])\nrequest(\n'https://example.com/leads',\npayload,\n'POST',\n['Content-Type: application/json']\n)\n
"},{"location":"webhookscript/examples.html#validate-request","title":"Validate request","text":"In this example, we use a common method of verifying webhooks by taking a hash of its contents concatenated to a secret. It demonstrates the way WebhookScript can get various information about the request by using the get_variable()
function, as well as string concatenation, hashing, if statements and returning responses with content, status codes and headers using respond()
, which halts execution.
verification_secret = \"JHRlc3RTY3JpcHRTZWNyZXQ\"\nverification_challenge = var(\"request.header.x-request-verification\")\nverification_result = hash(var(\"request.content\") + verification_secret, \"sha256\")\nif (!verification_challenge or verification_challenge != verification_result) {\nrespond(\"Invalid request\", 500)\n}\nrespond(\"Successful request\", 200)\n
"},{"location":"webhookscript/examples.html#send-a-x-www-form-urlencoded-request","title":"Send a x-www-form-urlencoded request","text":"content = query([\n'country': 'Cura\u00e7ao', 'population': 158665\n])\nheaders = ['Content-Type: application/x-www-form-urlencoded'];\nresponse = request('https://example.com', content, 'POST', headers);\n
"},{"location":"webhookscript/examples.html#transform-and-resend","title":"Transform and resend","text":"In the following, an incoming request is JSON decoded to an array, transformed and sent to \"Web Service 1\". Then the output is saved and passed on to \"Web Service 2\" in XML format. Basic error handling and validation is demonstrated.
// Configuration, fetched from the users' Global Variables in Control Panel\nws1_api_key = var('WS1_KEY')\nws2_user_token = var('WS1_USER_TOKEN')\n// Function for error handling which stops processing further actions/code and returns an error message\nfunction error (message) {\necho('Error: {}'.format(message))\nrespond(json_encode(['error': message]), 500)\n}\n// Parse original request\norig_req = json_decode(var('request.content'))\n// If the JSON was invalid\nif (!orig_req) {\nerror('Invalid request')\n}\n// Send request to Web Service 1, using format() for string placeholders\n// with JSON decoded values from the incoming request body\nws1_url = 'https://ws1.example.com/3.0/lists/{}/interest-categories/{}/interests'.format(\norig_req['listId'],\norig_req['groupId']\n)\nws1_content = [\n'first_name': orig_req['firstName'],\n'last_name': orig_req['lastName']\n]\nws1_response = request(\nws1_url,\njson_encode(ws1_content),\n'POST',\n['Authorization: Basic ' + ws1_api_key]\n)\n// Don't go further if the Web Service 1 step didn't succeed\nif (ws1_response['status'] != 200) {\necho(ws1_response['content']); // Log content to output\nerror('Invalid response from WS1')\n}\n// Get a value from the Web Service 1 request\nws1_response_id = json_decode(ws1_response)['id']\n// Pass response on to Web Service 2 in XML format, using a multi-line string and format()\nws2_content = '\n <qdbapi>\n <usertoken>{}</usertoken>\n <listid>{}</listid>\n <field fid=\"7\">{}</field>\n </qdbapi>'.format(ws2_user_token, orig_req['listId'], ws1_group_id)\nws2_response = request(\n'https://ws2.example.com/db/zzzzzz',\nws2_content,\n'POST', [\n\"Action: API_EditRecord\", \"Content-Type: application/xml\"\n]\n)\nif (ws2_response['status'] != 200) {\necho(ws2_response['content']); // Log content to debug log\nerror('Invalid response from WS2')\n}\n// Output the WS2 response content to debug output\necho(ws2_response['content'])\nrespond('OK', 200)\n
"},{"location":"webhookscript/examples.html#telegram-bot","title":"Telegram bot","text":"The Messaging service Telegram allows bots using their API. The general principle is this:
Note: Everywhere you see TELEGRAM_TOKEN
, replace it with the token you got from BotFather!
To create the Webhook subscription, change the token and the Webhook.site URL to your own and go to the following URL in your browser:
https://api.telegram.org/botTELEGRAM_TOKEN/setWebhook?url=https://webhook.site/a1351781
You should get a response similar to this:
{\n\"ok\": true,\n\"result\": true,\n\"description\": \"Webhook was set\"\n}\n
"},{"location":"webhookscript/examples.html#first-incoming-webhook","title":"First incoming Webhook","text":"When you add your bot to your Telegram contacts list, Telegram automatically sends a /start
command to the bot, which triggers a Webhook similar to this:
{\n\"update_id\": 176446573,\n\"message\": {\n\"message_id\": 1,\n\"from\": {\n\"id\": 2346545645,\n\"is_bot\": false,\n\"first_name\": \"Simon\",\n\"language_code\": \"en\"\n},\n\"chat\": {\n\"id\": 34534673234,\n\"first_name\": \"Simon\",\n\"type\": \"private\"\n},\n\"date\": 1581706369,\n\"text\": \"/start\",\n\"entities\": [\n{\n\"offset\": 0,\n\"length\": 6,\n\"type\": \"bot_command\"\n}\n]\n}\n}\n
You should be able to see this in the Webhook.site requests list.
From this, we have all the parts needed to build a script that answers to commands:
// Telegram API token\ntoken = 'TELEGRAM_TOKEN';\ncontent = json_decode(var('$request.content$'));\nmsg = content['message']['text'];\nresponse = \"Couldn't come up with anything witty.\";\nif (msg == \"How's it going?\") {\nresponse = 'Pretty good.'\n}\nif (msg == r\"You're (.*)\") {\nmatch = regex_extract_first(r\"You're (.*)\", msg)\nresponse = 'No, YOU are {}'.format(match);\n}\nif (msg == \"/start\") {\nresponse = \"Hi! I'm WebhookBot.\"\n}\nurl = 'https://api.telegram.org/bot{}/sendMessage'.format(token)\njson = [\n'chat_id': content['message']['chat']['id'],\n'text': response\n]\nrequest(url, json_encode(json), 'POST');\n
Things to note:
var()
function.request()
function.Simply copy this script into a WebhookScript Custom Action (remember to change the token!), and click Save Action.
Then, you can interact with the bot using the Telegram app:
And that's it! Congratulations on your bot. It's not very smart, but from here, the possibilities are endless!
"},{"location":"webhookscript/examples.html#building-html-content","title":"Building HTML content","text":"The following script builds a piece of HTML content using the string_format function, based on previously defined variables, and shows how to use a function to return different content based on input.
After this, it sends a JSON request (by converting an array to JSON via the json_encode function) containing the HTML using basic Bearer authentication.
function alert_class() {\nif (status == 'Operational') { return 'success'; }\nif (status == 'Degraded Performance') { return 'warning'; }\nif (string_contains(status, 'Disruption')) { return 'danger'; }\n}\ntemplate = '\n <div class=\"alert alert-{}\">\n <h2 class=\"alert-title\">{} - {}</h2>\n <p>\n {}<br />\n State: {}<br />\n Component affected: {}\n </p>\n <p>{}</p>\n </div>';\npostbody = string_format(\ntemplate,\nalert_class(),\nmessage,\nstate,\ncomponent,\nto_date('now').date_format('LLLL')\n)\njson = [\n'alert': [\n'title': '{} - {}'.format(component, state),\n'body': postbody,\n'draft': false,\n'types': ['alert']\n],\n]\nrequest(\n'http://example.com/alerts',\njson_encode(json),\n'POST',\n[\n'Content-Type: application/json',\n'Authorization: Basic {}'.format(token)\n]\n)\n
"},{"location":"webhookscript/examples.html#uploading-and-parsing-csv-file","title":"Uploading and parsing CSV file","text":"With this script, a file upload form is displayed when visiting the URL. After submitting the form, the CSV file is processed and validated (in this example, there must be more than 2 rows). If it can't be validated, an error message is shown. Finally, the user is shown an \"Upload successful\" message if the CSV file is valid.
url = var('request.url')\nset_header('content-type', 'text/html');\n// Display file upload form and exit if HTTP method is not POST\nif (var('request.method') != 'POST') {\nrespond('\n <html>\n <head><title>Upload CSV</title></head>\n <body>\n <h1>Upload CSV</h1>\n <form action=\"{}\" method=\"POST\" enctype=\"multipart/form-data\">\n <input type=\"file\" name=\"file\"/>\n <button type=\"submit\">Upload</button>\n </form>\n </body>\n </html>\n '.format(url))\n}\n// Use a comma as delimiter and treat first row (0) as header row\narray = csv_to_array(var('request.file.file.content'), ',', 0)\n// If CSV can't be parsed, or there's less than 2 rows, fail\nif (!array or array.length() < 2) {\nrespond('\n <h1>Could not parse CSV</h1>\n <a href=\"{}\">Upload again</a>\n '.format(url));\n}\n// Display the parsed CSV in JSON format \nrespond('\n <h1>Upload successful</h1>\n <pre>{}</pre>\n <p>\n <a href=\"{}\">Upload again</a>\n </p>\n'.format(json_encode(array), url))\n
"},{"location":"webhookscript/functions.html","title":"Functions","text":"The functions page has been split up in multiple sub-pages, please start here.
"},{"location":"webhookscript/reference.html","title":"Language Reference","text":"These are the functions that can be used in your script, and includes various utility functions and functions to interact with your Webhook.site URL.
"},{"location":"webhookscript/reference.html#syntax","title":"Syntax","text":"The language is built with a familiar PHP/JS/C-like syntax. Statements are separated using the ;
character, but this is only required for multiple statements on the same line:
Using semicolons (recommended):
a = 1 + 2; b = a + 3;\nc = b - a;\n
Using newlines:
a = 1 + 2\nb = a + 3\nc = b - a\n
The language has 6 data types:
Example usage:
a = true;\nb = 1 == 2; // false\nc = b == false; // true\nd = c == \"hello\"; // ERR: Cannot compare 'bool' and 'string'\ne = \"hello\" == r\"[0-9]\"; // false\nf = \"hello\" == r\"l{2}\"; // true\n
"},{"location":"webhookscript/reference.html#number","title":"Number","text":"Both integer and float values are supported.
Example usage:
a = 4 - 3; // 1\nb = a / 3; // 0.333...\n
"},{"location":"webhookscript/reference.html#string","title":"String","text":"This data types represents a series of characters. Multi-byte characters (accents, diacritics) are treated properly.
Example usage:
a = \"hello\";\nb = \"w\u00f3\u00f3\u00f3rld!\";\nc = a + \" \" + b; // \"hello w\u00f3\u00f3\u00f3rld!\"\n
"},{"location":"webhookscript/reference.html#regex","title":"Regex","text":"Regex data type exists for advanced string matching. It is defined using the r\"...\"
literal (a string literal prefixed with r
) and treated as PCRE (Perl-compatible) regular expressions (the same as within PHP itself) with Unicode mode enabled.
Example usage:
a = \"facebook\";\nb = r\"[o]{2}.*\";\nc = a == b; // true\n
"},{"location":"webhookscript/reference.html#array","title":"Array","text":"Arrays are untyped (PHP-style) containers that can accomodate multiple values of different (or same) types. Optionally, array index can be defined for a value. By default, integer index starting from the lowest index found (or from 0
) is used, but strings can also be used.
Example usage:
a = [\"abc\", 123, 4: true, false, r\"[A-Z]+\"];\n// Resulting array: [0: \"abc\", 1: 123, 4: true, 5: false, 6: r\"[A-Z]+\"]\nb = json_encode([\"the_number\": 123, \"other_numbers\": [42, 1337, 80085]])\n// Result:\n// {\n// \"the_number\": 123,\n// \"other_numbers\": [\n// 42,\n// 1337,\n// 80085\n// ]\n// }\n
"},{"location":"webhookscript/reference.html#ranges","title":"Ranges","text":"Simple way of defining ranges between numbers is provided via the a..[s..]b
range syntax.
a
is the from value.b
is the to value, inclusive, if it is not \"skipped\" due to a rather large step (see below).s
(optionally) is the step value which to use.Example usage:
a = 1..4; // (array) [0: 1, 1: 2, 4: 3, 5: 4]\nb = 1..2..4; // (array) [0: 1, 1: 3]\nc = 10..4..16; // (array) [0: 10, 1: 14]\n_x = 3\n_y = 6\n_z = 2\nd = _x.._z.._y; // (array) [0: 3, 1: 5]\n
Return value of such \"range literal\" is a new array
having values that are based on the range's parameters."},{"location":"webhookscript/reference.html#functions","title":"Functions","text":"Function is a value type that represents a \"unit\" of some self-contained logic. In WebhookScript they have their own type and are treated as first-class citizens: they can be stored inside variables and passed around as such. Direct invocation of an anonymous function is supported, provided that the anonymous function's definition is enclosed in parentheses. A function does capture its surrounding variables.
Example usage:
// Traditional definition.\nfunction sub(a, b) {\nreturn a - b;\n}\n// A variable \"sub\" that holds the \"sub()\" function is now defined in current scope.\nsub(1, 2); // Returns -1\n// Storing a function value into a variable.\n// Note: This is equivalent to the previous definition.\nsub_2 = function(a, b) {\nreturn a - b;\n};\n// A variable \"sub_2\" that holds the \"sub_2()\" function is now defined in current scope.\nsub_2(1, 2); // Returns -1\n// Creating a function with alternative, short syntax.\n// Note: This is equivalent all of the previous definitions.\nsub_3 = (a, b) => {\nreturn a - b;\n};\n// A variable \"sub_3\" that holds the \"sub_3()\" function is now defined in current scope.\nsub_3(1, 2); // Returns -1\n// Creating and using an anonymous function directly.\n// Using an alternative, short syntax.\n((a, b) => {\nreturn a - b;\n})(1, 2); // Returns -1\n
"},{"location":"webhookscript/reference.html#chained-functions","title":"Chained functions","text":"In addition to classical function invocation, WebhookScript additionaly supports Uniform Function Call Syntax (UFCS) as a way to call functions \"on values\". Essentially, it means that calling foo(bar);
is equivalent to calling bar.foo()
, or - to provide an example with additional parameters - that calling foo(bar, 1, true, \"something\");
is equivalent to calling bar.foo(1, true, \"something\")
.
When using chained function invocation, the interpreter will try to find the most fitting function to call. \"Most fitting\" meaning that when the client calls bar()
function on a value having the string
type, WebhookScript will try to find and use the string_bar()
first. If such function is not defined, only then will the interpreter use the original bar()
function.
Consider this a syntactic sugar to make coding in WebhookScript a bit more user-friendly. Because of this the user is able to call \"something\".length()
on a string the same way as calling [1, 2, 3].length()
on an array, even though there are in fact two separate functions string_length()
and array_length()
invoked behind the scenes.
Plethora of well known operators can be used to define relationships between and/or affect various values. Different operators can have various effects on various data types, some of which are covered down below.
"},{"location":"webhookscript/reference.html#precedence","title":"Precedence","text":"Precedence of various operators is defined as follows (from highest to lowest): - (
... )
(parentheses) - !
(logical not) - *
, /
(multiplication/division) - +
, -
(addition/subtraction) - ==
, !=
, >=
, <=
, >
, <
(comparison) - and
(logical and) - or
(logical not) - =
(assignment)
1 + 2 + 3 + 4
((1 + 2) + 3) + 4
1 - 2 + 3 - 4
((1 - 2) + 3) - 4
1 + 2 * 3 + 4
1 + (2 * 3) + 4
1 + 2 * 3 / 4
1 + ((2 * 3) / 4)
1 + -2 * 3 / 4
1 + (((-2) * 3) / 4)
1 and 2 or 3 and 4
(1 and 2) or (3 and 4)
1 or 2 or 3 and 4
(1 or 2) or (3 and 4)
1 or !2 or !3 and 4
(1 or (!2)) or ((!3) and 4))
x = true or false and true
x = (true or (false and true))
x = a == b
x = (a == b)
x = a > 5 and b < 6
x = ((a > 5) and (b < 6))
"},{"location":"webhookscript/reference.html#logical-operators","title":"Logical operators","text":"and
true
if both operands are truthy.false
.or
true
if either one (or both) operand is truthy.false
.!
a = true;\nb = !a; // false\nc = !b; // true\nd = !!b; // false\n
=
a = 1;\nb = \"a word\";\nc = false;\nd = r\"regul[ar]+\";\ne = [\"x\", \"b\": \"z\"];\ne[\"c\"] = \"x\"; // e == [\"x\", \"b\": \"z\", \"c\": \"x\"]\n
+
, -
+
Add two numbers.-
Subtract two numbers.+
Concatenate two strings.-
Removes all occurences of the right side from the left side.-
(if the right side is a Regex value) Removes all matches of the regex from the left side string.a = 5 + 4 // (number) 9\nb = 5 - 4; // (number) 1\nc = \"a word and number \" + 5.to_string(); // (string) \"a word and number 5\"\nd = \"a word and number {}\".format(5); // (string) \"a word and number 5\"\ne = \"a word\" + \" and one more\"; // (string) \"a word and one more\"\nf = \"a word\" - \"or\"; // (string) \"a wd\"\ng = \"regular expressions\" - r\"regul[ar]+\\s*\"; // (string) \"expressions\"\n_x = 5 + \"4\" // ERR: Cannot use operator '+' with 'number' and 'string'\n_x = \"a word and number \" + 5; // ERR: Cannot use operator '+' with 'number' and 'string'\n
*
, /
+
Multiply two numbers.-
Divide two numbers.a = 1 * -2; // (number) -2\nb = 2 * 3; // (number) 6\nc = 2 * \"3\"; // (string) \"33\"\nd = \"3\" * 4; // (string) \"3333\"\ne = 5 / 4; // (number) 1.25\ng = 5 / 5; // (number) 1\n_x = \"2\" * \"3\"; // ERR: Cannot use operator '*' with 'string' and 'string'\n_x = 5 / \"4\"; // ERR: Cannot use operator '/' with 'number' and 'string'\n_x = \"20\" / 4; // ERR: Cannot use operator '/' with 'string' and 'number'\n
You can control the flow of your program with several kinds of statements the language provides.
"},{"location":"webhookscript/reference.html#if-construct","title":"if construct","text":"The if
construct - as in all other programming languages - allows you to dynamically branch your program flow based on some conditions during runtime.
a = true;\nif (a) {\nb = 1;\n}\n// b == 1\n
c = false;\nif (c) {\nd = 1;\n} else {\nd = 2;\n}\n// d == 2\n
a = false;\nb = true;\nc = 5;\nif (a or b) {\nd = 1;\nif (d < c) {\ne = d + c;\n}\n}\n// d == 1, e == 6\n
"},{"location":"webhookscript/reference.html#for-construct","title":"for construct","text":"The for
construct allows you to iterate over a value that supports it (array
or string
values), while performing a task on that collection's single item.
txt = \"123456789\";\nresult = [];\nfor (n in txt) {\nif (5 > n.to_number() > 0) {\nresult.push(n);\n}\n}\n// result == [0: \"1\", 1: \"2\", 2: \"3\", 3: \"4\"]\n
prices = [100, 200, 300, 600, 1200];\nsentence_template = \"This costs {} units of money!\";\nresults = [];\nfor (price in prices) {\nresults.push(sentence_template.format(price));\n}\n// results == [\n// 0: \"This costs 100 units of money!\",\n// 1: \"This costs 200 units of money!\",\n// 2: \"This costs 300 units of money!\",\n// 3: \"This costs 600 units of money!\",\n// 4: \"This costs 1200 units of money!\"\n// ]\n
Note: The flow of program inside the for
cycle can be controlled further by using the continue
and break
statements.
The while
construct does a thing if a specified condition is met (if the condition expression has a truthy value).
c = 0;\nwhile (c < 100) {\ntotal = total + c;\nc = c + 1;\n}\n// total == 4950\n
Note: The flow of program inside the while
cycle can be controlled further by using the continue
and break
statements.
Returns true or false depending on whether array contains a value equal to needle.
To check whether a key exists, use the array_has
function.
employees = [6547: 'Simon', 235345: 'Jack', 4657: 'Jim']\ndd(array_contains(employees, 'Simon'))\n// -> true\n
"},{"location":"webhookscript/functions/array.html#array_chunkarray-array-number-count-bool-preserve_keys-false-array","title":"array_chunk(array array, number count, bool preserve_keys = false) : array","text":"Splits a single array into chunks of count
. When preserve_keys
is set to true
, the array keys are preserved.
test_arr = [\n'a': 123,\n'b': 234,\n'c': 345,\n'd': 345, 'e': 456\n]\ndump(array_chunk(test_arr, 2, true))\n// -> [0: [\"a\": 123, \"b\": 234], 1: [\"c\": 345, \"d\": 345], 2: [\"e\": 456]]\ndump(array_chunk(test_arr, 2, false))\n// -> [0: [0: 123, 1: 234], 1: [0: 345, 1: 345], 2: [0: 456]]\n
"},{"location":"webhookscript/functions/array.html#array_copyarray-array-array","title":"array_copy(array array) : array","text":"Returns a copy of array
"},{"location":"webhookscript/functions/array.html#array_diffarray-array1-array-array2-array","title":"array_diff(array array1, array array2) : array","text":"Returns the items of array1 that are not present in array2 while keeping the array indices.
"},{"location":"webhookscript/functions/array.html#array_getarray-array-stringnumber-index-any-default-any","title":"array_get(array array, string/number index, any default) : any","text":""},{"location":"webhookscript/functions/array.html#array_hasarray-array-stringnumber-key-bool","title":"array_has(array array, string/number key) : bool","text":"Returns true if array
contains key
, and false if it does not.
To check whether a value exists, use the array_contains
function.
employees = [6547: 'Simon', 235345: 'Jack', 4657: 'Jim']\ndd(array_has(employees, 235345))\n// -> true\n
"},{"location":"webhookscript/functions/array.html#array_joinarray-array-string-joiner-string","title":"array_join(array array, string joiner) : string","text":"Returns a string where all the values are joined by joiner
.
['hello', 'world'].join(',')\n-> \"hello,world\"\n
"},{"location":"webhookscript/functions/array.html#array_keysarray-array-array","title":"array_keys(array array) : array","text":"Returns the keys of an array.
"},{"location":"webhookscript/functions/array.html#array_lengtharray-array-number","title":"array_length(array array) : number","text":""},{"location":"webhookscript/functions/array.html#array_maparray-array-func-function","title":"array_map(array array, func function)","text":"Runs function with each array value, and returns array with key as result.
employees = ['Simon', 'Jack', 'Jim']\nresult = array_map(employees, function (employee) {\nreturn 'Hello, '+employee+'!'\n})\ndd(result)\n// -> [0: \"Hello, Simon!\", 1: \"Hello, Jack!\", 2: \"Hello, Jim!\"]\n
"},{"location":"webhookscript/functions/array.html#array_mergearray-array1-array-array2-array","title":"array_merge(array array1, array array2): array","text":"Merges 2 arrays into a single array.
test1 = [123, 234, 345]\ntest2 = [345, 456]\ndump(array_merge(test1, test2))\n// -> [0: 123, 1: 234, 2: 345, 3: 345, 4: 456]\n
"},{"location":"webhookscript/functions/array.html#array_number_ofarray-stringnumber-value-number","title":"array_number_of(array, string/number value) : number","text":"Returns amount of value
"},{"location":"webhookscript/functions/array.html#array_poparray-array-any","title":"array_pop(array array) : any","text":"Pop element off end of array
"},{"location":"webhookscript/functions/array.html#array_pusharray-array-any-value-any","title":"array_push(array array, any value) : any","text":"Adds value to end of array and returns value
"},{"location":"webhookscript/functions/array.html#array_randomarray-array-any","title":"array_random(array array) : any","text":"Returns random value of array
"},{"location":"webhookscript/functions/array.html#array_rangearray-array-int-offset-int-length-0-bool-preserve_keys-false-array","title":"array_range(array array, int offset, ?int length = 0, ?bool preserve_keys = false) : array","text":"Returns a range of array
, starting from offset
, and returns the amount specified in length
if set (otherwise until the end of the array.)
If preserve_keys
is true, the result has the array keys preserved.
array = ['one', 'two', 'three', 'four', 'five']\ndump(array_slice(array, 2))\n// [0: \"three\", 1: \"four\", 2: \"five\"]\ndump(array_slice(array, 2, 2))\n// [0: \"three\", 1: \"four\"]\ndump(array_range(array, -3, 2))\n// [0: \"three\", 1: \"four\"]\n
"},{"location":"webhookscript/functions/array.html#array_reversearray-array-array","title":"array_reverse(array array) : array","text":"Returns array in reverse order
employees = [6547: 'Simon', 235345: 'Jack', 4657: 'Jim']\ndd(array_reverse(employees))\n// -> [0: \"Jim\", 1: \"Jack\", 2: \"Simon\"]\n
"},{"location":"webhookscript/functions/array.html#array_shufflearray-array-array","title":"array_shuffle(array array) : array","text":"Returns shuffled version of array
"},{"location":"webhookscript/functions/array.html#array_sortarray-array-array","title":"array_sort(array array) : array","text":"Sorts array by its values. Keys are kept as-is.
"},{"location":"webhookscript/functions/array.html#array_splicearray-array-int-offset-int-length-array-replacement-array","title":"array_splice(array array, int offset, ?int length, array replacement) : array","text":"Removes length
amount of values from array
, starting from offset
(if offset
is negative, starts from the end of the array.)
If replacement
is specified, the removed values are replaced with it.
array = ['one', 'two', 'three', 'four', 'five']\ndump(array_splice(array, 2, 2))\n// [0: \"one\", 1: \"two\", 2: \"five\"]\ndump(array_splice(array, 2, 2, ['hello']))\n// [0: \"one\", 1: \"two\", 2: \"hello\", 3: \"five\"]\n
"},{"location":"webhookscript/functions/array.html#array_valuesarray-array-array","title":"array_values(array array) : array","text":"Returns the values of an array.
employees = [6547: 'Simon', 235345: 'Jack', 4657: 'Jim']\ndd(array_values(employees))\n// -> [0: \"Simon\", 1: \"Jack\", 2: \"Jim\"]\n
"},{"location":"webhookscript/functions/array.html#to_arrayarray-array-array","title":"to_array(array array) : array","text":"Returns array.
"},{"location":"webhookscript/functions/bool.html","title":"Booleans","text":""},{"location":"webhookscript/functions/bool.html#bool_andbool-value1-bool-value2-bool","title":"bool_and(bool value1, bool value2): bool","text":"Returns value1 && value2
Returns !value
Returns true
if:
\"\"
(empty string)[]
(empty array)null
Returns true
if value is null
. An alternative to if (value == null)
that won't break when value
isn't null due to type checking.
Example:
value = 'example'\nif (is_null(value)) {\n// Won't trigger a type error\n}\n
"},{"location":"webhookscript/functions/bool.html#to_boolstringnumberarraybool-value-bool","title":"to_bool(string/number/array/bool value) : bool","text":"Casts value
to a bool.
In WebhookScript, dates are not a specific type, but rather expressed as strings that WebhookScript will attempt to parse using a very powerful date parsing engine.
WebhookScript supports a variety of date formats, and functions taking a date will attempt to guess the format of the input string in order to parse the date into a ISO-8601 format
If possible, it's recommended to use the ISO-8601 format, for example 2020-05-27T04:00:00.000000Z
.
List of date format characters for the to_date
and date_format
functions.
List of locales/translations available for date display functions
List of Timezone names
In addition to automatically recognizing date strings like ISO-8601, WebhookScript has ability to recognize the following special formats.
now
+4 day
, -2 month
- adds or subtracts to the current date and time, can be suffixed to other datessecond
, minute
, hour
, day
, fortnight
, week
, month
, year
next Thursday
last Monday
first day of January 2008
first Saturday of July 2008
Monday next week
@1215282385
- UNIX timestampFor more information, PHP Supported Date and Time Formats.
"},{"location":"webhookscript/functions/date.html#nowstring-timezone-string","title":"now(?string timezone) : string","text":"Returns the current date in ISO-8601 format, using timezone
, if specified.
now()\n// -> 2022-12-15T11:00:00.000000Z\nnow('America/Los_Angeles')\n// -> 2022-12-15T03:00:00.000000-08:00\n
"},{"location":"webhookscript/functions/date.html#to_datestring-date-string-input_format-locale-locale-string-timezone-bool-keep_timezone-false-string","title":"to_date(string date, ?string input_format, ?locale locale, ?string timezone, bool keep_timezone = false): ?string","text":"Returns a ISO-8601 formatted date string in UTC time from the provided date
string.
If specified, input_format
is used to parse the date without having to guess the format (see the Date Format Characters specification.) Otherwise, see Recognized date formats.
If the keep_timezone
parameter is set to true, the resulting date string will keep the timezone. The locale
parameter will attempt to parse the date using the specified locale.
If the date is invalid or could not be guessed, null
is returned.
// Current date and time\n'now'.to_date()\n// -> 2020-11-25T00:00:00.000000Z\n// Relative formats\n'first monday august 2019'.to_date()\n// Automatic format guessing\n'2020-01-01 23:02:01'.to_date()\n// Timezone handling\n'2020-01-01 23:02:01'.to_date(null, null, 'GMT-5')\n// -> \"2020-01-02T04:02:01.000000Z\", interpreted as GMT-5 and converted to UTC\n'2020-01-01 23:02:01'.to_date(null, null, 'GMT-5', true)\n// -> \"2020-01-01T23:02:01.000000-05:00\", date keeps timezone\n// Unix timestamp\n'@1215282385'.to_date()\n// Custom date format\n'2/4/12 06:03'.to_date('M/D/YY HH:mm')\n// -> 2012-02-04T06:03:00.000000Z\n// To escape characters in the format string, backslashes can be used\n'2020-01-05 12h30m15s'.to_date('YYYY-MM-DD HH\\\\hmm\\\\mss\\\\s')\n// -> 2020-01-05T12:30:15.000000Z\n
"},{"location":"webhookscript/functions/date.html#date_formatstring-date-string-format-string-locale-string-timezone-string","title":"date_format(string date, ?string format, ?string locale, ?string timezone): string","text":"Returns a date converted to the format specified in format
. date
is automatically parsed and can be a date string (ISO-8601 recommended) or one of the recognized date formats.
For a full list of date format characters, see the Date Format Characters specification.
If format
is not specified, a default human readable readable string is returned.
date_format('2008-07-05T18:26:25.000000Z', 'YYYY-MM-DD') // -> 2008-07-05\ndate_format('2008-07-05T18:26:25.000000Z', 'LLLL', 'da') // -> l\u00f8rdag d. 5. juli 2008 kl. 18:26\ndate_format('2020-01-01T23:02:01.000000-05:00', 'LLLL', null, 'GMT+2') // -> Thursday, January 2, 2020 6:02 AM\ndate_format('now', 'x')\n// -> 1606329669220 (current date in UNIX timestamp with microseconds)\n// Add 1 hour to an existing date (see Recognized date formats above)\ndate_format('2021-10-28 11:28:55 +1 hour', 'YYYY-MM-DD HH:mm:ss')\n// -> 2021-10-28 12:28:55\n
"},{"location":"webhookscript/functions/date.html#date_to_arraystring-array","title":"date_to_array(string): array","text":"Returns an array containing all the components of a given date.
dump(date_to_array('2008-07-05T18:26:25.324542Z'))\n// [\n// \"year\": 2008,\n// \"month\": 7,\n// \"day\": 5,\n// \"dayOfWeek\": 6,\n// \"dayOfYear\": 187,\n// \"hour\": 18,\n// \"minute\": 26,\n// \"second\": 25,\n// \"micro\": 324542,\n// \"timestamp\": 1215282385,\n// \"formatted\": \"2008-07-05 18:26:25\",\n// \"timezone\": \"Z\"\n// ]\n
"},{"location":"webhookscript/functions/date.html#date_intervalstring-date1-string-date2-string-format-stringint","title":"date_interval(string date1, ?string date2, ?string format): string/int","text":"Calculates the interval between date1
and date2
. When date2
is unspecified/null, now
is used.
If no format string is specified, the interval is returned as the number of seconds between the dates, with the number being negative if date2
is before date1
.
For the format
string, the PHP DateInterval
format specification is used.
date_interval('2008-07-16T23:13:26.234212Z', '2008-07-05T18:26:25.324542Z') // -> -967620\ndate_interval(\n'2008-07-16T23:13:26.234212Z',\n'2008-07-05T18:26:25.324542Z',\n'%d days, %h hours, %i minutes'\n)\n// -> 11 days, 4 hours, 47 minutes\n
"},{"location":"webhookscript/functions/date.html#date_interval_humanstring-date1-string-date2-string-locale-stringint","title":"date_interval_human(string date1, ?string date2, ?string locale): string/int","text":"Formats the difference between 2 dates in a way that's easy to read for humans.
If no locale is specified, English is used. When date2
is unspecified/null, now
is used.
date_interval_human(\n'2008-07-16T23:13:26.234212Z',\n'2008-07-05T18:26:25.324542Z'\n)\n// -> 1 week after\ndate_interval_human(\n'2008-07-16T23:13:26.234212Z',\n'2008-07-05T18:26:25.324542Z',\n'es'\n)\n// -> 1 semana despu\u00e9s\n
"},{"location":"webhookscript/functions/files.html","title":"File handling","text":"When you send an email, or send a multipart/form-data request, files are extracted as the file.*.*
variables, but you can also loop over files using WebhookScript.
Returns an array of files, with the following keys:
dump(files())\n// [\n// 0: [\n// \"id\": \"76b7274a-e806-4f72-ba49-85ad05926ef0\", \n// \"filename\": \"Screen Shot 2020-06-05 at 2.15.29 PM.png\", \n// \"size\": 1203671, \n// \"content_type\": \"image/png\"\n// ]\n// ]\n// Filtering files by type\nfor (file in files()) {\nif (r'.*\\.png'.match(file['filename'])) {\ndump(file['filename'] + ' is png.')\n}\n}\n// \"Screen Shot 2020-06-05 at 2.15.29 PM.png is png.\"\n
"},{"location":"webhookscript/functions/files.html#file_contentstring-fileid-string","title":"file_content(string fileId) : string","text":"Returns the content of a specific file, using the id
key from the files()
function above.
firstFile = array_get(files(), 0)\nfileContent = file_contents(firstFile['id']);\n
"},{"location":"webhookscript/functions/flow.html","title":"Flow Control and Responses","text":""},{"location":"webhookscript/functions/flow.html#script-execution","title":"Script Execution","text":""},{"location":"webhookscript/functions/flow.html#actionstring-action_type-array-parameters-array","title":"action(string action_type, array parameters) : array","text":"Runs Custom Actions in WebhookScript. Can be used to loop over items in a way that not possible using the visual Custom Actions editor.
The function returns an array of runtime variables which can be used to fetch the result of an action that sets a variable, like google_sheets_get_values
.
The action_type
s and their parameters can be seen here.
The following action extracts a list of files from an array of objects in a JSON array and uploads each file to Dropbox using the dropbox_upload_file
action.
Example
In this example, we assume that the request content sent to the Webhook.site URL contains the following:
{\n\"files\": [\n{\n\"url\": \"https://example.com/path/980e683e-9bd7-4512-bc0e-acfd6e022a81\",\n\"name\": \"example_1.png\"\n},\n{\n\"url\": \"https://example.com/path/c8e1282f-46a7-45ab-9699-0472ff9ab96c\",\n\"name\": \"example_2.png\"\n}\n]\n}\n
In the script, the request contents are JSON decoded and looped over. For each item, the file's URL is downloaded and a Dropbox Upload File action is executed.
files = json_path(var('request.content'), '.files.*')\nfor (file_info in files) {\n// Download file from URL\nfile_download = request(file_info['url'])\n// Generate a destination path for the Dropbox file\ndestination_path = '/Example Files/%s'.format(file_info['name'])\n// Execute Dropbox Upload File action\naction(\n'dropbox_upload_file',\n[\n'path': destination_path,\n'body': file_download['content'],\n'mode': 'update',\n'provider_id': providerId\n]\n)\n}\n
"},{"location":"webhookscript/functions/flow.html#delayint-seconds-string-code-deprecated","title":"delay(int seconds, string code) [deprecated]","text":"Warning
This function has been deprecated and should no longer be used. As an alternative, it's recommended that you mark a WebhookScript Custom Action as Queued. More info here.
Executes code in the future. Any output will be stored on the request and will show with a \"Was delayed\" label.
The code will not inherit the execution scope.
In this example, the format
function is used to prepare the code string with a URL, causing {}
to be replaced with with https://example.com
.
code = '\n request(\n \"{}\",\n \\'{\"message\": \"Hello World!\"}\\',\n \"POST\"\n )\n'\nurl = 'https://example.com'\ndelay(5, code.format(url));\n
The maximum amount of seconds allowed is 604800 (7 days).
"},{"location":"webhookscript/functions/flow.html#dont_save","title":"dont_save()","text":"Marks the request so it is not saved in Webhook.site, which is useful when receiving a large amount of requests. The request can still be seen when it comes in, but will not be available through through the app later, or through the API. The action is useful in cases where e.g. a URL receive a large amount of requests.
"},{"location":"webhookscript/functions/flow.html#execstring-code-any","title":"exec(string code) : any","text":"Executes code in code
and returns the result. The code will inherit the execution scope.
Downloads code located at url
and returns the result. The code will inherit the execution scope.
As an example, this can be used if you want to re-use code. Just upload it to a server or e.g. Github and use it in different WebhookScript actions.
result = import('https://raw.githubusercontent.com/webhooksite/scripts/ec22946a83ea85f607fcc6bff83f9d81ed2fe4ed/hello_world.ws')\necho(result) // value\n
"},{"location":"webhookscript/functions/flow.html#stop","title":"stop()","text":"Stops Custom Action execution.
"},{"location":"webhookscript/functions/flow.html#responses","title":"Responses","text":""},{"location":"webhookscript/functions/flow.html#respondstring-content-int-status-array-headers","title":"respond(string content, int status, array headers)","text":"Stops Custom Action execution and return a response immediately.
"},{"location":"webhookscript/functions/flow.html#set_contentstring-content","title":"set_content(string content)","text":"Sets or overwrites the response content of the URL. Script execution continues.
"},{"location":"webhookscript/functions/flow.html#set_headerstring-header_name-string-header_value","title":"set_header(string header_name, string header_value)","text":"Sets or overwrites a response header of the URL. Script execution continues.
"},{"location":"webhookscript/functions/flow.html#set_responsestring-content-int-status-array-headers","title":"set_response(string content, int status, array headers)","text":"Sets or overwrites response content, status and headers in single function. Script execution continues.
headers
should be an array of strings e.g. [\"X-Example: Value\", \"X-Foo: Bar\"]
.
Sets or overwrites the HTTP response status of the current URL. Script execution continues.
"},{"location":"webhookscript/functions/general.html","title":"General","text":""},{"location":"webhookscript/functions/general.html#about-functions","title":"About Functions","text":"These are the functions that can be used in your script, and includes various utility functions and functions to interact with your Webhook.site URL.
"},{"location":"webhookscript/functions/general.html#chaining","title":"Chaining","text":"Functions can be chained directly to a primitive (strings, numbers, arrays).
These two statements are equivalent:
'Hello World'.echo()\n
echo('Hello World')\n
They can even be chained, for example:
'Hello World'.hash('md5').echo()
Furthermore, functions that begin in a type can be referenced without it, for example, when calling the format
function with the first argument being a string, the language infers that actually the string_format
function should be used.
echo(string_format('hello %s', 'world')) // hello world\n'hello %s'.format('world').echo() // hello world\n
Read more about functions in the reference.
"},{"location":"webhookscript/functions/general.html#custom-functions","title":"Custom functions","text":"Define your own functions like this:
function sub(a, b) {\nreturn a - b;\n}\n
Read more about functions in the reference.
"},{"location":"webhookscript/functions/general.html#debugging-and-output","title":"Debugging and output","text":""},{"location":"webhookscript/functions/general.html#echostring-string","title":"echo(...string string)","text":"Adds string
to script debug output.
Stops Custom Action execution and adds value
to script debug output.
Adds value
as a decoded string to script debug output.
Returns the type name of a value, e.g. \"string\"
.
Converts a binary tring to a hexadecimal representation.
"},{"location":"webhookscript/functions/math.html#hex2binstring-hex_string-string","title":"hex2bin(string hex_string) : ?string","text":""},{"location":"webhookscript/functions/math.html#num2hexnumber-number-string","title":"num2hex(number number) : string","text":""},{"location":"webhookscript/functions/math.html#hex2numstring-hex_string-number","title":"hex2num(string hex_string) : number","text":""},{"location":"webhookscript/functions/math.html#general","title":"General","text":""},{"location":"webhookscript/functions/math.html#absnumber-number-number","title":"abs(number number) : number","text":""},{"location":"webhookscript/functions/math.html#ceilnumber-number-number","title":"ceil(number number) : number","text":"Rounds a number up to nearest integer.
"},{"location":"webhookscript/functions/math.html#floornumber-number-number","title":"floor(number number) : number","text":"Rounds a number down to nearest integer.
"},{"location":"webhookscript/functions/math.html#is_numericany-value-bool","title":"is_numeric(any value) : bool","text":"Returns true
if value
is numeric.
Examples:
12345
returns true
.\"1.0\"
returns true
.null
returns false
.Returns the remainder after number is divided by divisor
.
Returns the value of Pi.
"},{"location":"webhookscript/functions/math.html#pownumber-number-number-power-number","title":"pow(number number, number power) : number","text":""},{"location":"webhookscript/functions/math.html#randnumber-min-number-max-number","title":"rand(number min, number max) : number","text":"Returns a random number between min
and max
.
Rounds a number up with specified precision (number of digits after decimal point.)
round(1.39, 1) // -> 1.4\n
"},{"location":"webhookscript/functions/math.html#sqrtnumber-number-number","title":"sqrt(number number) : number","text":""},{"location":"webhookscript/functions/math.html#to_numberany-value-number","title":"to_number(any value) : number","text":""},{"location":"webhookscript/functions/network.html","title":"Network and HTTP","text":""},{"location":"webhookscript/functions/network.html#http","title":"HTTP","text":""},{"location":"webhookscript/functions/network.html#queryarray-form_values-string","title":"query(array form_values) : string","text":"Converts an associative array into a form-style string, used for e.g. application/x-www-form-urlencoded
requests or HTTP query strings.
query(['country': 'Cura\u00e7ao', 'population': 158665]) // country=Cura%C3%A7ao&population=158665\n
"},{"location":"webhookscript/functions/network.html#httpstring-url-array-options-array","title":"http(string url, array options) : array","text":"Sends a HTTP request and returns an array with the following keys containing response data:
content
(contains a cURL error message in case of an error)status
(null
in case of an error)headers
url
The options
array can contain the following keys; none are required.
mode
- string, one of: text (default), multipart, urlencoded, forwardcontent
- string, body content, used when mode is textmethod
- string, HTTP method, e.g. POSTmultipart
- array of arrays, used when mode is multipart, containing keys:name
- required, string, form item namefilename
- stringcontent-type
- stringcontent
- stringurlencoded
- array of arrays, used when mode is urlencoded, containing keys:name
- required, string, form item namevalue
- stringheaders
- array of strings, HTTP headersskip_ssl_verification
- default false, skips TLS/SSL certificiate validation for HTTPS requeststimeout
- number, default 5, timeout in secondsTo send a simple POST request containing e.g. JSON data, use the following:
http(\n'https://example.com',\n[\n'method': 'POST', 'content': '{\"example\": \"json\"}', 'headers': ['Content-Type: application/json']\n]\n)\n
"},{"location":"webhookscript/functions/network.html#formmultipart-request","title":"form/multipart request","text":"http(\n'https://example.com', [\n'method': 'POST',\n'mode': 'multipart', 'multipart': [\n[\n'name': 'value1',\n'content': 'This is a value',\n]\n]\n]\n)\n
"},{"location":"webhookscript/functions/network.html#requeststring-url-string-body-string-method-get-array-headers-bool-override-false-timeout-5-array","title":"request(string url, string body, string method = 'GET', array headers, bool override = false, timeout = 5) : array","text":"Sends a HTTP request and returns an array with the following keys containing response data:
content
(contains a cURL error message in case of an error)status
(null
in case of an error)headers
url
The headers should be an array of strings, for example:
[\n'Content-Type: application/json',\n'Accept: application/json, text/plain, */*'\n]\n
To get a JSON document, validate if valid JSON, and get a property:
response = request('https://example.com')\ndecoded = json_decode(response['content'])\nif (decoded) {\nvalue = decoded['value']\n}\n
If override
is set to true, none of the content from the original request is included (e.g. query strings, headers, content.)
Sends a HTTP Multipart request, e.g. for uploading files.
name
(the form name value) and content
are required in the items
array; the rest is optional.
timeout
specifies the request timeout in seconds.
The return value is an array with the following keys containing response data:
content
(contains a cURL error message in case of an error)status
(null
in case of an error)headers
url
Example:
multipart(\n'https://example.com/file-upload', [\n[\n'name': 'file[]',\n'filename': 'file1.txt',\n'content': 'hello world',\n'headers': ['Header1': 'value', 'Header2': 'othervalue']\n],\n[\n'name': 'client_id',\n'content': 'abcd123',\n]\n],\n'POST',\n['Api-Key: xxxx']\n)\n
"},{"location":"webhookscript/functions/network.html#url_decodestring-value-string","title":"url_decode(string value) : string","text":"Returns an URL-decoded version of value.
"},{"location":"webhookscript/functions/network.html#url_encodestring-value-string","title":"url_encode(string value) : string","text":"Returns an URL-encoded version of value.
url_encode('here\\'s a value') // here%27s+a+value\n
"},{"location":"webhookscript/functions/string.html","title":"Strings","text":""},{"location":"webhookscript/functions/string.html#general-string-functions","title":"General string functions","text":""},{"location":"webhookscript/functions/string.html#string_containsstring-subject-numberstringregex-value-bool","title":"string_contains(string subject, number/string/regex value) : bool","text":"Returns boolean if subject contains value
"},{"location":"webhookscript/functions/string.html#string_find_firststring-subject-numberstring-value-number","title":"string_find_first(string subject, number/string value) : number","text":"Returns position of value in subject, or false if not found
"},{"location":"webhookscript/functions/string.html#string_find_laststring-subject-numberstring-value-number","title":"string_find_last(string subject, number/string value) : number","text":"Returns position of value in subject, or false if not found
"},{"location":"webhookscript/functions/string.html#string_formatstring-formatstring-any-items-string","title":"string_format(string formatString, ...any items) : string","text":"Sprintf-like formatting of formatString with items, see PHP sprintf docs.
"},{"location":"webhookscript/functions/string.html#string_joinstring-subject-array-items-numberstringboolarray-string","title":"string_join(string subject, array items (number/string/bool/array)) : string","text":"Joins items with string subject
"},{"location":"webhookscript/functions/string.html#string_lengthstring-string-number","title":"string_length(string string) : number","text":"Returns length of string (multibyte-aware)
"},{"location":"webhookscript/functions/string.html#string_lowerstring-string-string","title":"string_lower(string string) : string","text":"Converts string
to lowercase (multibyte-aware)
Returns number value of string
"},{"location":"webhookscript/functions/string.html#string_randomnumber-length-string","title":"string_random(number length) : string","text":"Returns a random string of length using the A\u2013Z,a\u2013z,0\u20139
alphabet.
Replaces string search with replace found in subject.
string_replace('this is a sentence', 'sentence', 'string') // -> this is a string\nstring_replace('this is great', r'(.*) is (.*)', '$1 was $2') // -> this was great\n
"},{"location":"webhookscript/functions/string.html#string_replacestring-subject-array-replace_pairs-string","title":"string_replace(string subject, array replace_pairs) : string","text":"Replaces items found in subject using the replace_pairs array:
string_replace(\"hello, it's good\", [\n'hello': 'hi',\n'good': 'great'\n]) // -> hi, it's great\n
"},{"location":"webhookscript/functions/string.html#string_reversestring-subject-string","title":"string_reverse(string subject) : string","text":"Reverses string subject
"},{"location":"webhookscript/functions/string.html#string_slicestring-subject-number-from-number-to-null-string","title":"string_slice(string subject, number from, number to = null) : string","text":"Extracts a segment of a string. Multibyte-aware.
string_slice('hello world', 0, 5)
returns hello
.
string_slice('hello world', 6)
returns world
.
Returns string where the individual characters has been shuffled.
"},{"location":"webhookscript/functions/string.html#string_splitstring-subject-stringregex-delimiter-array","title":"string_split(string subject, string/regex delimiter) : array","text":"Returns array of split string subject with delimiter
"},{"location":"webhookscript/functions/string.html#string_titlestring-string-string","title":"string_title(string string) : string","text":"Returns string
converted to title case.
string_title('hello world')
returns Hello World
.
Converts string
to UPPERCASE (multibyte-aware).
Converts a regex string to a regex type
"},{"location":"webhookscript/functions/string.html#to_stringstringnumberbool-value-string","title":"to_string(string/number/bool value) : string","text":"Returns value as string
"},{"location":"webhookscript/functions/string.html#trimstring-string-string","title":"trim(string string): string","text":"Returns string
with space, tab and newline characters removed from the beginning and end of the string.
Returns a random v4 UUID.
"},{"location":"webhookscript/functions/string.html#csv","title":"CSV","text":""},{"location":"webhookscript/functions/string.html#csv_to_arraystring-content-string-delimiter-int-header_offset-string-enclosure-string-escape-array","title":"csv_to_array(string content, string delimiter, ?int header_offset, string enclosure, string escape) : array","text":"Takes a CSV string and outputs to an array, with each row being an item in the array.
content
should be a string containing the CSV document.delimiter
will explicitly set the CSV delimiter the parser will attempt to use (e.g. ;
). Must be a single character. Defaults to ,
(comma.)header_offset
, when specified, causes the output array item's keys to be set to the header values. Setting to 0
will mark the first row as the header row. enclosure
sets the field enclosure character. Must be a single character. Defaults to \"
(double quote.)escape
sets the field escape character. Must be a single character. Defaults to \\
(backslash.)csv_content = 'firstname,lastname,title\n\"M. J.\",Plumley,\"Sr. Developer\"\nEmily,\"Jenna Platt\",\"Chief Information Officer\"'\narray = csv_to_array(csv_content, ',', 0)\necho(json_encode(array))\n// [\n// {\n// \"firstname\": \"John\",\n// \"lastname\": \"Doe\",\n// \"title\": \"Sr. Developer\"\n// },\n// {\n// \"firstname\": \"Emily\",\n// \"lastname\": \"Jenna Platt\",\n// \"title\": \"Chief Information Officer\"\n// }\n// ]\n
"},{"location":"webhookscript/functions/string.html#base64","title":"Base64","text":""},{"location":"webhookscript/functions/string.html#base64_decodestring-string-string","title":"base64_decode(string string) : string","text":"Returns a base64-decoded string.
"},{"location":"webhookscript/functions/string.html#base64url_decodestring-string-string","title":"base64url_decode(string string) : string","text":"Returns a base64url-decoded string.
If the base64 string was encoded using URL-friendly base64url
format, this function should be used rather the regular base64_decode
function.
Returns base64-encoded string.
"},{"location":"webhookscript/functions/string.html#base64url_encodestring-string-string","title":"base64url_encode(string string) : string","text":"Returns a URL-friendly base64url-encoded string, where characters +
, /
have been replaced by -
and _
, and any =
padding characters have been removed.
Returns a hashed version of value
using the algo
algorithm.
'hello world'.hash('md5') // 5eb63bbbe01eeed093cb22bb8f5acdc3\n
The following built-in algorithms are available: md2
, md4
, md5
, sha1
, sha224
, sha256
, sha384
, sha512/224
, sha512/256
, sha512
, sha3-224
, sha3-256
, sha3-384
, sha3-512
, ripemd128
, ripemd160
, ripemd256
, ripemd320
, whirlpool
, tiger128,3
, tiger160,3
, tiger192,3
, tiger128,4
, tiger160,4
, tiger192,4
, snefru
, snefru256
, gost
, gost-crypto
, adler32
, crc32
, crc32b
, fnv132
, fnv1a32
, fnv164
, fnv1a64
, joaat
, haval128,3
, haval160,3
, haval192,3
, haval224,3
, haval256,3
, haval128,4
, haval160,4
, haval192,4
, haval224,4
, haval256,4
, haval128,5
, haval160,5
, haval192,5
, haval224,5
, haval256,5
.
Returns the calculated message digest/hash in a hexadecimal formatted as a string.
For a list of possible values for algo
, see hash()
above.
See also binary and hexadecimal conversion functions.
In the following example, the hash needs to be converted to binary, and then to base64, to match the signature.
hmac = hmac(var('request.content'), 'sha256', 'insert_secret_here');\nsignature = var('request.header.x-signature', '');\nhash = base64_encode(hex2bin(hmac))\nif (hash != signature) {\nrespond('Unauthorized', 401);\n}\n
"},{"location":"webhookscript/functions/string.html#cryptography","title":"Cryptography","text":"List of available algorithms for functions in the Cryptography section:
blake2b512
blake2s256
md4
md5
md5-sha1
ripemd160
sha1
sha224
sha256
sha3-224
sha3-256
sha3-384
sha3-512
sha384
sha512
sha512-224
sha512-256
shake128
shake256
sm3
whirlpool
Signs value
using private_key
and algo
. Returns signature on success, false
on failure.
For an example using sign() for JWT authorization, see here.
"},{"location":"webhookscript/functions/string.html#digeststring-value-string-algo-falsestring","title":"digest(string value, string algo) : false/string","text":"Returns a cryptographic digest of value
using algo
. Returns false
on failure.
Verifies value
using signature
, public_key
and algo
Returns a string with all HTML tags removed.
html_strip_tags('<b>test</b>')
returns test
.
Converts HTML to plaintext. A more aggressive version of html_strip_tags
.
html_to_text('<b>test</b>')
returns test
.
Decodes all HTML entities (for example,
) to normal characters.
Replaces characters in a string with HTML encoded versions.
"},{"location":"webhookscript/functions/string.html#markdown_to_htmlstring-string-bool-safe_mode-false-string","title":"markdown_to_html(string string, bool safe_mode = false) : string","text":"Converts a Markdown string to HTML.
markdown_to_html('# Hello world')
returns <h1>Hello world</h1>
.
If safe_mode
is set to true, characters like <
, >
are HTML-entity encoded.
Decodes json
and returns an array.
array = json_decode(var('request.content'))\n
"},{"location":"webhookscript/functions/string.html#json_encodearray-array-bool-format-true-string","title":"json_encode(array array, bool format = true) : string","text":"Takes an array and encodes it as a JSON string.
Per default, the JSON array is formatted with whitespace. To turn it off, set format
to false.
Returns the result of a json
string parsed using the JSONPath functionality.
Per default, if there's just one match (e.g. if matching on a property value that's a string), this value is returned. To always return an array, set return_first
to false.
dump(json_path('{\"v\": []}', 'v[*]', false))\ndump(json_path('{\"v\": []}', 'v[*]'))\n// []\n// \"\"\ndump(json_path('{\"v\": [\"item1\"]}', 'v[*]', false))\ndump(json_path('{\"v\": [\"item1\"]}', 'v[*]'))\n// [0: \"item1\"]\n// \"item1\"\ndump(json_path('{\"v\": [\"item1\", \"item2\"]}', 'v[*]', false))\ndump(json_path('{\"v\": [\"item1\", \"item2\"]}', 'v[*]'))\n// [0: \"item1\", 1: \"item2\"]\n// [0: \"item1\", 1: \"item2\"]\n
"},{"location":"webhookscript/functions/string.html#json_escapestring-json-string","title":"json_escape(string json) : string","text":"JSON-escapes all special JSON characters like double quotes, newlines, etc.
"},{"location":"webhookscript/functions/string.html#regex","title":"Regex","text":""},{"location":"webhookscript/functions/string.html#preg_matchstring-regex-string-subject-arrayfalse","title":"preg_match(string regex, string subject) : array/false","text":"Returns the matching values as an array, or false
if the regex did not match. Supports regex pattern modifiers.
// Returns anything enclosed within <html> tags in a string\nhtml = preg_match('/<html>(.*)<\\\\/html>/s', content)\n
"},{"location":"webhookscript/functions/string.html#regex_extractregex-regex-string-subject-arrayfalse","title":"regex_extract(regex regex, string subject) : array/false","text":"Returns the matching string and all match groups as an array, and false
on failure.
input = \"You're a good bot\"\noutput = regex_extract(r\"You're (\\w) (.*)\", input)\ndump(output) // [0: \"You're a good bot\", 1: \"a\", 2: \"good bot\"]\n
"},{"location":"webhookscript/functions/string.html#regex_extract_firstregex-regex-string-subject-any-default-stringfalse","title":"regex_extract_first(regex regex, string subject, any default) : string/false","text":"Returns the first match group of a regex, and false
(or default
, if set) on failure.
input = \"You're a good bot\"\noutput = regex_extract(r\"You're (.*)\", input)\ndump(output) // \"a good bot\"\ninput = \"Hello world\"\noutput = regex_extract(r\"You're (.*)\", input, 'no value')\ndump(output) // \"no value\"\n
"},{"location":"webhookscript/functions/string.html#regex_matchregex-regex-string-subject-stringfalse","title":"regex_match(regex regex, string subject) : string/false","text":"Returns the first matching string, otherwise false.
input = \"You're a good bot\"\noutput = regex_match(r\"You're .*\", input)\ndump(output) // \"You're a good bot\"\n
"},{"location":"webhookscript/functions/string.html#regex_to_stringregex-regex-string","title":"regex_to_string(regex regex) : string","text":"Returns the regex converted to a string
"},{"location":"webhookscript/functions/string.html#xml","title":"XML","text":""},{"location":"webhookscript/functions/string.html#xml2arraystring-xml-array","title":"xml2array(string xml) : array","text":"Converts XML to a WebhookScript array. This can be useful for parsing XML, or e.g. converting XML to JSON.
Note that this function is opinionated - there's no one way to convert XML to an array as XML has more features than WebhookScript arrays. Therefore the function attempts to carry over all the data by automatically generating properties like @value
and @attributes
depending on the input data.
For example, the following XML:
<orders>\n<record>\n<Name type=\"full\">John Doe</Name>\n<Reference>49690</Reference>\n</record>\n<record>\n<Name type=\"username\">someone@example.com</Name>\n<Reference>49690</Reference>\n</record>\n</orders>\n
Converts to JSON via the following script:
json_encode(xml2array(var('request.content')))\n
Which returns:
{\n\"orders\": {\n\"record\": [\n{\n\"Name\": {\n\"@value\": \"John Doe\",\n\"@attributes\": {\n\"type\": \"full\"\n}\n},\n\"Reference\": 49690\n},\n{\n\"Name\": {\n\"@value\": \"someone@example.com\",\n\"@attributes\": {\n\"type\": \"username\"\n}\n},\n\"Reference\": 49690\n}\n]\n}\n}\n
"},{"location":"webhookscript/functions/string.html#xpathstring-xpath-string-input-stringnull","title":"xpath(string xpath, string input): string/null","text":"Returns the first result of an XPath query on XML document input
.
Given a request with the following content:
<?xml version=\"1.0\"?>\n<organization name=\"ExampleCo\">\n<employees>\n<employee id=\"1\">Jack</employee>\n<employee id=\"2\">Ann</employee>\n</employees>\n</organization>\n
xpath(var('$request.content$'), '//employee[1]') // returns \"Jack\"
var('$request.content$').xpath('//employee[1]') // returns \"Jack\"
More information and examples regarding XPath.
"},{"location":"webhookscript/functions/string.html#xpath_allstring-xpath-string-input-stringnull","title":"xpath_all(string xpath, string input): string/null","text":"Returns the results of an XPath query on XML document input
as an array.
Given a request with the following content:
<?xml version=\"1.0\"?>\n<organization name=\"ExampleCo\">\n<employees>\n<employee id=\"1\">Jack</employee>\n<employee id=\"2\">Ann</employee>\n</employees>\n</organization>\n
xpath_all(var('$request.content$'), '//employee]') // returns [0: \"Jack\", 1: \"Ann\"]
var('$request.content$').xpath('//employee') // returns [0: \"Jack\", 1: \"Ann\"]
More information and examples regarding XPath.
"},{"location":"webhookscript/functions/string.html#special-string-functions","title":"Special string functions","text":""},{"location":"webhookscript/functions/string.html#convert_kanastring-mode-string","title":"convert_kana(string, mode) : string","text":"Performs a \"han-kaku\" - \"zen-kaku\" conversion for string string. This function is only useful for Japanese. See here for more info.
"},{"location":"webhookscript/functions/variables.html","title":"Custom Action Variables","text":"These functions lets you interface with other Custom Actions by getting and setting variables from them. These functions are also how you retrieve Global Variables defined in the Control Panel.
"},{"location":"webhookscript/functions/variables.html#varstring-variable_name-stringnumber-default-mixed","title":"var(string variable_name, ?string/number default) : mixed","text":"Retrieves the value of a Variable or Global Variable (defined in the Control Panel). The surrounding dollar signs are not mandatory.
Returns null
(or the value of default
) if the variable does not exist.
var('request.header.x-request-verification') // returns value of the `x-request-verification` header\n
"},{"location":"webhookscript/functions/variables.html#setstring-variable_name-string-variable_value","title":"set(string variable_name, string variable_value)","text":"Exports a Variable for use in downstream actions, like the Set Runtime Variable action.
For example, the following code:
set('myvar', 'Hello world')\n
Would cause the string $myvar$
to be replaced with Hello world
in all subsequent actions.
Permanently creates or updates a Global Variable (as defined in Control Panel.)
The value can also be retrieved with the var()
function in subsequent action executions.
Deletes a global variable.
"},{"location":"webhookscript/functions/variables.html#variables-array","title":"variables : array","text":"A variable (not a function) containing an associative array with all available Webhook.site variables.
user_agent = variables['request.header.user-agent']\n
"}]}
\ No newline at end of file
diff --git a/sitemap.xml b/sitemap.xml
new file mode 100644
index 0000000..011c79d
--- /dev/null
+++ b/sitemap.xml
@@ -0,0 +1,208 @@
+
+WebhookScript uses the ISO format for converting and formatting dates, and the format is compatible with the Moment.js format method.
+The following examples are based on the date 2017-01-05 17:04:05.084512
.
Code | +Example | +Description | +
---|---|---|
OD | +5 | +Day number with alternative numbers such as 三 for 3 if locale is ja_JP | +
OM | +1 | +Month number with alternative numbers such as ၀၂ for 2 if locale is my_MM | +
OY | +2017 | +Year number with alternative numbers such as ۱۹۹۸ for 1998 if locale is fa | +
OH | +17 | +24-hours number with alternative numbers such as ႑႓ for 13 if locale is shn_MM | +
Oh | +5 | +12-hours number with alternative numbers such as 十一 for 11 if locale is lzh_TW | +
Om | +4 | +Minute number with alternative numbers such as ୫୭ for 57 if locale is or | +
Os | +5 | +Second number with alternative numbers such as 十五 for 15 if locale is ja_JP | +
D | +5 | +Day of month number (from 1 to 31) | +
DD | +05 | +Day of month number with trailing zero (from 01 to 31) | +
Do | +5th | +Day of month with ordinal suffix (from 1st to 31th), translatable | +
d | +4 | +Day of week number (from 0 (Sunday) to 6 (Saturday)) | +
dd | +Th | +Minified day name (from Su to Sa), transatable | +
ddd | +Thu | +Short day name (from Sun to Sat), transatable | +
dddd | +Thursday | +Day name (from Sunday to Saturday), transatable | +
DDD | +5 | +Day of year number (from 1 to 366) | +
DDDD | +005 | +Day of year number with trailing zeros (3 digits, from 001 to 366) | +
DDDo | +5th | +Day of year number with ordinal suffix (from 1st to 366th), translatable | +
e | +4 | +Day of week number (from 0 (Sunday) to 6 (Saturday)), similar to "d" but this one is translatable (takes first day of week of the current locale) | +
E | +4 | +Day of week number (from 1 (Monday) to 7 (Sunday)) | +
H | +17 | +Hour from 0 to 23 | +
HH | +17 | +Hour with trailing zero from 00 to 23 | +
h | +5 | +Hour from 0 to 12 | +
hh | +05 | +Hour with trailing zero from 00 to 12 | +
k | +17 | +Hour from 1 to 24 | +
kk | +17 | +Hour with trailing zero from 01 to 24 | +
L | +04/09/1986 | +Date (in local format) | +
LL | +September 4 1986 | +Month name, day of month, year | +
LLL | +September 4 1986 8:30 PM | +Month name, day of month, year, time | +
LLLL | +Thursday, September 4 1986 8:30 PM | +Day of week, month name, day of month, year, time | +
LT | +8:30 PM | +Time (without seconds) | +
LTS | +8:30:00 PM | +Time (with seconds) | +
m | +4 | +Minute from 0 to 59 | +
mm | +04 | +Minute with trailing zero from 00 to 59 | +
a | +pm | +Meridiem am/pm | +
A | +PM | +Meridiem AM/PM | +
s | +5 | +Second from 0 to 59 | +
ss | +05 | +Second with trailing zero from 00 to 59 | +
S | +0 | +Second tenth | +
SS | +08 | +Second hundredth (on 2 digits with trailing zero) | +
SSS | +084 | +Millisecond (on 3 digits with trailing zeros) | +
SSSS | +0845 | +Second ten thousandth (on 4 digits with trailing zeros) | +
SSSSS | +08451 | +Second hundred thousandth (on 5 digits with trailing zeros) | +
SSSSSS | +084512 | +Microsecond (on 6 digits with trailing zeros) | +
SSSSSSS | +0845120 | +Second ten millionth (on 7 digits with trailing zeros) | +
SSSSSSSS | +08451200 | +Second hundred millionth (on 8 digits with trailing zeros) | +
SSSSSSSSS | +084512000 | +Nanosecond (on 9 digits with trailing zeros) | +
M | +1 | +Month from 1 to 12 | +
MM | +01 | +Month with trailing zero from 01 to 12 | +
MMM | +Jan | +Short month name, translatable | +
MMMM | +January | +Month name, translatable | +
Mo | +1st | +Month with ordinal suffix from 1st to 12th, translatable | +
Q | +1 | +Quarter from 1 to 4 | +
Qo | +1st | +Quarter with ordinal suffix from 1st to 4th, translatable | +
G | +2017 | +ISO week year (see ISO week date) | +
GG | +2017 | +ISO week year (on 2 digits with trailing zero) | +
GGG | +2017 | +ISO week year (on 3 digits with trailing zeros) | +
GGGG | +2017 | +ISO week year (on 4 digits with trailing zeros) | +
GGGGG | +02017 | +ISO week year (on 5 digits with trailing zeros) | +
g | +2017 | +Week year according to locale settings, translatable | +
gg | +2017 | +Week year according to locale settings (on 2 digits with trailing zero), translatable | +
ggg | +2017 | +Week year according to locale settings (on 3 digits with trailing zeros), translatable | +
gggg | +2017 | +Week year according to locale settings (on 4 digits with trailing zeros), translatable | +
ggggg | +02017 | +Week year according to locale settings (on 5 digits with trailing zeros), translatable | +
W | +1 | +ISO week number in the year (see ISO week date) | +
WW | +01 | +ISO week number in the year (on 2 digits with trailing zero) | +
Wo | +1st | +ISO week number in the year with ordinal suffix, translatable | +
w | +1 | +Week number in the year according to locale settings, translatable | +
ww | +01 | +Week number in the year according to locale settings (on 2 digits with trailing zero) | +
wo | +1st | +Week number in the year according to locale settings with ordinal suffix, translatable | +
x | +1483635845085 | +Millisecond-precision timestamp (same as date.getTime() in JavaScript) | +
X | +1483635845 | +Timestamp (number of seconds since 1970-01-01) | +
Y | +2017 | +Full year from -9999 to 9999 | +
YY | +17 | +Year on 2 digits from 00 to 99 | +
YYYY | +2017 | +Year on 4 digits from 0000 to 9999 | +
YYYYY | +02017 | +Year on 5 digits from 00000 to 09999 | +
YYYYYY | ++002017 | +Year on 5 digits with sign from -09999 to +09999 | +
z | +UTC | +Abbreviated time zone name | +
zz | +UTC | +Time zone name | +
Z | ++00:00 | +Time zone offset HH:mm | +
ZZ | ++0000 | +Time zone offset HHmm | +
Source: Carbon Docs
+Info
+Do you have a nice example to share with other users? Or looking for even more examples? Take a look at the WebhookScript example repository, and make a pull request if you want to contribute: Webhook.site Script Repository
+The following script uses the hmac() function and the sha256
algorithm to verify a HMAC signature, which is commonly used to verify webhooks. In this example, the secret is secret
and the signature comes from the incoming request's x-signature
HTTP header.
If the signature doesn't match, further action execution is stopped and the URL immediately respond with a 401 status code.
+hmac = hmac(var('request.content'), 'sha256', 'secret');
+signature = var('request.header.x-signature', '');
+
+if (hmac != signature) {
+ respond('Unauthorized', 401);
+}
+
An example acquiring an access token from the DocuSign API using JWT and the WebhookScript sign()
function:
private_key = '-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBA...
+-----END RSA PRIVATE KEY-----';
+
+header = json_encode([
+ 'alg': 'RS256',
+ 'typ': 'JWT'
+])
+
+currentTimestamp = now().date_format('X').to_number()
+
+body = json_encode([
+ "iss": "00000000-0000-0000-0000-000000000000",
+ "sub": "00000000-0000-0000-0000-000000000000",
+ "aud": "account-d.docusign.com",
+ "iat": currentTimestamp,
+ "exp": currentTimestamp + 6000,
+ "scope": "impersonation signature"
+])
+
+token = base64url_encode(header) + "." + base64url_encode(body)
+signature = base64url_encode(sign(token, private_key, 'sha256'))
+jwt = token + "." + signature
+
+content = query([
+ 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
+ 'assertion': jwt
+])
+headers = ['Content-Type: application/x-www-form-urlencoded']
+response = request('https://account-d.docusign.com/oauth/token', content, 'POST', headers)
+
+if (response['status'] != 200) {
+ echo('Could not get token: ' + response['content'])
+ stop()
+}
+
+response_json = json_decode(response['content'])
+echo('Got access token: ' + response_json['access_token'])
+
In this example, if we assume the variable $mydate$
is set to 2021-07-26T16:23:50+03:00
, the variable will be overwritten to 2021.07.26 16:23
for actions running after the WebhookScript action.
input = var('$mydate$')
+
+output = date_format(input, 'YYYY.MM.DD HH:mm')
+
+set('$mydate$', output)
+
More information about available date format characters.
+This script uses Global Variables to keep track of the amount of requests sent to a Webhook.site URL within the last 10 minutes.
+Once the 10 minutes are up, it sends a request to another endpoint with the amount of requests and the date, and then it resets the counter.
+period = 600 // 10 minutes in seconds
+currentPeriod = var('current_period', now())
+counter = var('counter', 0).to_number()
+
+currentPeriodSecs = date_interval(currentPeriod)
+
+if (currentPeriodSecs < period) {
+ counter = counter + 1
+ store('counter', counter)
+} else {
+ // Above 10 minutes, reset and send request
+ store('counter', 0)
+ store('current_period', now())
+ request(
+ 'https://webhook.site/14fea227-60ab-4291-b679-12b8c104c78e',
+ json_encode([
+ "counter": counter,
+ "timestamp": now(),
+ ]),
+ 'POST'
+ )
+}
+
+echo('count : {}'.format(counter))
+echo('period: {}'.format(currentPeriodSecs))
+
This example shows various ways of iterating through JSON arrays, comparing data and rearranging it to another format.
+First, it loops through the items
array, verifying that the item
must have a group ID that a specific one from an array.
Second, it changes the fields
array from an array of objects to a key-value object using the name
and value
fields.
// Define input as a JSON string
+json = '{
+ "items": [
+ {
+ "first_name": "Jack",
+ "last_name": "Daniels",
+ "phone": "+1 100-555-999",
+ "group_ids": [346, 46456, 23423],
+ "fields": [
+ {
+ "id": 45698,
+ "name": "birthday",
+ "value": "1990-01-01"
+ },
+ {
+ "id": 344,
+ "name": "car",
+ "value": "BMW M2"
+ }
+ ]
+ },
+ {
+ "first_name": "Jim",
+ "last_name": "Beam",
+ "phone": "+1 123-555-788",
+ "group_ids": [3456, 43546, 234234, 456456],
+ "fields": [
+ {
+ "id": 45698,
+ "name": "birthday",
+ "value": "1987-05-01"
+ },
+ {
+ "id": 344,
+ "name": "car",
+ "value": "Toyota Corolla"
+ },
+ {
+ "id": 248,
+ "name": "nickname",
+ "value": "Jimmie"
+ }
+ ]
+ }
+ ]
+}'
+
+// Decode to array
+data = json_decode(json)
+
+// Define an array of valid groups
+valid_groups = [43546, 999999]
+
+// Loop over items
+for (item in data['items']) {
+ // Per default, item does not have a valid group.
+ has_valid_group = false
+
+ // Loop through each group ID in the data
+ for (group_id in item['group_ids']) {
+ // Does the "valid_groups" array contain this group ID?
+ if (valid_groups.contains(group_id)) {
+ has_valid_group = true
+ }
+ }
+
+ if (has_valid_group) {
+ echo(item['first_name'] + ' did not have a valid group.')
+ } else {
+ echo(item['first_name'] + ' has a valid group.')
+ }
+
+ // Extract "fields" into an array of key => value
+ fields = []
+ for (field in item['fields']) {
+ fields[field['name']] = field['value']
+ }
+
+ echo(json_encode(fields))
+}
+
Returns the following output:
+Jack has a valid group.
+{
+ "birthday": "1990-01-01",
+ "car": "BMW M2"
+}
+Jim did not have a valid group.
+{
+ "birthday": "1987-05-01",
+ "car": "Toyota Corolla",
+ "nickname": "Jimmie"
+}
+
In this example, we loop through a series of items and pick the item that's contained in a string.
+location = 'test ABC example';
+
+compares = [
+ '123': 'token1',
+ 'ABC': 'token2',
+ 'DEF': 'token3',
+]
+
+token = ''; // Default value
+
+for (compare in array_keys(compares)) {
+ if (location.contains(compare)) {
+ token = compares[compare]
+ }
+}
+
+dump(token) // token2
+
If you're building a JSON object, we recommend doing it in WebhookScript instead of typing JSON in the Send Request action type (If you do anyway, we recommend using the .json
Variable Modifier, More info here).
In this example, one of the JSON values contain HTML generated using the string_format function.
+html_template = '<b><u>New {} lead</u></b><br>
+<br>
+Location: {}<br>
+Message from customer:<br>
+<div style="background:#CCC">{}</div>'
+
+html_message = string_format(
+ html_template,
+ var('lead_type'),
+ var('location'),
+ var('message')
+)
+
+payload = json_encode([
+ 'lead': [
+ 'firstname': var('firstname'),
+ 'lastname': var('lastname'),
+ 'html': html_message
+ ]
+])
+
+
+request(
+ 'https://example.com/leads',
+ payload,
+ 'POST',
+ ['Content-Type: application/json']
+)
+
In this example, we use a common method of verifying webhooks by taking a hash of its contents concatenated to a secret. It demonstrates the way WebhookScript can get various information about the request by using the get_variable()
function, as well as string concatenation, hashing, if statements and returning responses with content, status codes and headers using respond()
, which halts execution.
verification_secret = "JHRlc3RTY3JpcHRTZWNyZXQ"
+verification_challenge = var("request.header.x-request-verification")
+verification_result = hash(var("request.content") + verification_secret, "sha256")
+
+if (!verification_challenge or verification_challenge != verification_result) {
+ respond("Invalid request", 500)
+}
+
+respond("Successful request", 200)
+
content = query([
+ 'country': 'Curaçao',
+ 'population': 158665
+])
+headers = ['Content-Type: application/x-www-form-urlencoded'];
+response = request('https://example.com', content, 'POST', headers);
+
In the following, an incoming request is JSON decoded to an array, transformed and sent to "Web Service 1". Then the output is saved and passed on to "Web Service 2" in XML format. Basic error handling and validation is demonstrated.
+// Configuration, fetched from the users' Global Variables in Control Panel
+ws1_api_key = var('WS1_KEY')
+ws2_user_token = var('WS1_USER_TOKEN')
+
+// Function for error handling which stops processing further actions/code and returns an error message
+function error (message) {
+ echo('Error: {}'.format(message))
+ respond(json_encode(['error': message]), 500)
+}
+
+// Parse original request
+orig_req = json_decode(var('request.content'))
+
+// If the JSON was invalid
+if (!orig_req) {
+ error('Invalid request')
+}
+
+// Send request to Web Service 1, using format() for string placeholders
+// with JSON decoded values from the incoming request body
+ws1_url = 'https://ws1.example.com/3.0/lists/{}/interest-categories/{}/interests'.format(
+ orig_req['listId'],
+ orig_req['groupId']
+)
+
+ws1_content = [
+ 'first_name': orig_req['firstName'],
+ 'last_name': orig_req['lastName']
+]
+ws1_response = request(
+ ws1_url,
+ json_encode(ws1_content),
+ 'POST',
+ ['Authorization: Basic ' + ws1_api_key]
+)
+
+// Don't go further if the Web Service 1 step didn't succeed
+if (ws1_response['status'] != 200) {
+ echo(ws1_response['content']); // Log content to output
+ error('Invalid response from WS1')
+}
+
+// Get a value from the Web Service 1 request
+ws1_response_id = json_decode(ws1_response)['id']
+
+// Pass response on to Web Service 2 in XML format, using a multi-line string and format()
+ws2_content = '
+ <qdbapi>
+ <usertoken>{}</usertoken>
+ <listid>{}</listid>
+ <field fid="7">{}</field>
+ </qdbapi>'.format(ws2_user_token, orig_req['listId'], ws1_group_id)
+
+ws2_response = request(
+ 'https://ws2.example.com/db/zzzzzz',
+ ws2_content,
+ 'POST',
+ [
+ "Action: API_EditRecord",
+ "Content-Type: application/xml"
+ ]
+)
+
+if (ws2_response['status'] != 200) {
+ echo(ws2_response['content']); // Log content to debug log
+ error('Invalid response from WS2')
+}
+
+// Output the WS2 response content to debug output
+echo(ws2_response['content'])
+respond('OK', 200)
+
The Messaging service Telegram allows bots using their API. The general principle is this:
+Note: Everywhere you see TELEGRAM_TOKEN
, replace it with the token you got from BotFather!
To create the Webhook subscription, change the token and the Webhook.site URL to your own and go to the following URL in your browser:
+https://api.telegram.org/botTELEGRAM_TOKEN/setWebhook?url=https://webhook.site/a1351781
+You should get a response similar to this:
+ +When you add your bot to your Telegram contacts list, Telegram automatically sends a /start
command to the bot, which triggers a Webhook similar to this:
{
+ "update_id": 176446573,
+ "message": {
+ "message_id": 1,
+ "from": {
+ "id": 2346545645,
+ "is_bot": false,
+ "first_name": "Simon",
+ "language_code": "en"
+ },
+ "chat": {
+ "id": 34534673234,
+ "first_name": "Simon",
+ "type": "private"
+ },
+ "date": 1581706369,
+ "text": "/start",
+ "entities": [
+ {
+ "offset": 0,
+ "length": 6,
+ "type": "bot_command"
+ }
+ ]
+ }
+}
+
You should be able to see this in the Webhook.site requests list.
+From this, we have all the parts needed to build a script that answers to commands:
+// Telegram API token
+token = 'TELEGRAM_TOKEN';
+
+content = json_decode(var('$request.content$'));
+msg = content['message']['text'];
+response = "Couldn't come up with anything witty.";
+
+if (msg == "How's it going?") {
+ response = 'Pretty good.'
+}
+
+if (msg == r"You're (.*)") {
+ match = regex_extract_first(r"You're (.*)", msg)
+ response = 'No, YOU are {}'.format(match);
+}
+
+if (msg == "/start") {
+ response = "Hi! I'm WebhookBot."
+}
+
+url = 'https://api.telegram.org/bot{}/sendMessage'.format(token)
+
+json = [
+ 'chat_id': content['message']['chat']['id'],
+ 'text': response
+]
+
+request(url, json_encode(json), 'POST');
+
Things to note:
+var()
function.request()
function.Simply copy this script into a WebhookScript Custom Action (remember to change the token!), and click Save Action.
+ +Then, you can interact with the bot using the Telegram app:
+ +And that's it! Congratulations on your bot. It's not very smart, but from here, the possibilities are endless!
+The following script builds a piece of HTML content using the string_format function, based on previously defined variables, and shows how to use a function to return different content based on input.
+After this, it sends a JSON request (by converting an array to JSON via the json_encode function) containing the HTML using basic Bearer authentication.
+function alert_class() {
+ if (status == 'Operational') { return 'success'; }
+ if (status == 'Degraded Performance') { return 'warning'; }
+ if (string_contains(status, 'Disruption')) { return 'danger'; }
+}
+
+template = '
+ <div class="alert alert-{}">
+ <h2 class="alert-title">{} - {}</h2>
+ <p>
+ {}<br />
+ State: {}<br />
+ Component affected: {}
+ </p>
+ <p>{}</p>
+ </div>';
+
+postbody = string_format(
+ template,
+ alert_class(),
+ message,
+ state,
+ component,
+ to_date('now').date_format('LLLL')
+)
+
+json = [
+ 'alert': [
+ 'title': '{} - {}'.format(component, state),
+ 'body': postbody,
+ 'draft': false,
+ 'types': ['alert']
+ ],
+]
+
+request(
+ 'http://example.com/alerts',
+ json_encode(json),
+ 'POST',
+ [
+ 'Content-Type: application/json',
+ 'Authorization: Basic {}'.format(token)
+ ]
+)
+
With this script, a file upload form is displayed when visiting the URL. After submitting the form, the CSV file is processed and validated (in this example, there must be more than 2 rows). If it can't be validated, an error message is shown. Finally, the user is shown an "Upload successful" message if the CSV file is valid.
+url = var('request.url')
+set_header('content-type', 'text/html');
+
+// Display file upload form and exit if HTTP method is not POST
+if (var('request.method') != 'POST') {
+ respond('
+ <html>
+ <head><title>Upload CSV</title></head>
+ <body>
+ <h1>Upload CSV</h1>
+ <form action="{}" method="POST" enctype="multipart/form-data">
+ <input type="file" name="file"/>
+ <button type="submit">Upload</button>
+ </form>
+ </body>
+ </html>
+ '.format(url))
+}
+
+// Use a comma as delimiter and treat first row (0) as header row
+array = csv_to_array(var('request.file.file.content'), ',', 0)
+
+// If CSV can't be parsed, or there's less than 2 rows, fail
+if (!array or array.length() < 2) {
+ respond('
+ <h1>Could not parse CSV</h1>
+ <a href="{}">Upload again</a>
+ '.format(url));
+}
+
+// Display the parsed CSV in JSON format
+respond('
+ <h1>Upload successful</h1>
+ <pre>{}</pre>
+ <p>
+ <a href="{}">Upload again</a>
+ </p>
+'.format(json_encode(array), url))
+
The functions page has been split up in multiple sub-pages, please start here.
+ +Returns true or false depending on whether array contains a value equal to needle.
+To check whether a key exists, use the array_has
function.
employees = [6547: 'Simon', 235345: 'Jack', 4657: 'Jim']
+
+dd(array_contains(employees, 'Simon'))
+// -> true
+
Splits a single array into chunks of count
. When preserve_keys
is set to true
, the array keys are preserved.
test_arr = [
+ 'a': 123,
+ 'b': 234,
+ 'c': 345,
+ 'd': 345,
+ 'e': 456
+]
+
+dump(array_chunk(test_arr, 2, true))
+// -> [0: ["a": 123, "b": 234], 1: ["c": 345, "d": 345], 2: ["e": 456]]
+
+dump(array_chunk(test_arr, 2, false))
+// -> [0: [0: 123, 1: 234], 1: [0: 345, 1: 345], 2: [0: 456]]
+
Returns a copy of array
+Returns the items of array1 that are not present in array2 while keeping the array indices.
+Returns true if array
contains key
, and false if it does not.
To check whether a value exists, use the array_contains
function.
employees = [6547: 'Simon', 235345: 'Jack', 4657: 'Jim']
+
+dd(array_has(employees, 235345))
+// -> true
+
Returns a string where all the values are joined by joiner
.
Returns the keys of an array.
+Runs function with each array value, and returns array with key as result.
+employees = ['Simon', 'Jack', 'Jim']
+
+result = array_map(employees, function (employee) {
+ return 'Hello, '+employee+'!'
+})
+
+dd(result)
+
+// -> [0: "Hello, Simon!", 1: "Hello, Jack!", 2: "Hello, Jim!"]
+
Merges 2 arrays into a single array.
+test1 = [123, 234, 345]
+test2 = [345, 456]
+
+dump(array_merge(test1, test2))
+
+// -> [0: 123, 1: 234, 2: 345, 3: 345, 4: 456]
+
Returns amount of value
+Pop element off end of array
+Adds value to end of array and returns value
+Returns random value of array
+Returns a range of array
, starting from offset
, and returns the amount specified in length
if set (otherwise until the end of the array.)
If preserve_keys
is true, the result has the array keys preserved.
array = ['one', 'two', 'three', 'four', 'five']
+
+dump(array_slice(array, 2))
+// [0: "three", 1: "four", 2: "five"]
+
+dump(array_slice(array, 2, 2))
+// [0: "three", 1: "four"]
+
+dump(array_range(array, -3, 2))
+// [0: "three", 1: "four"]
+
Returns array in reverse order
+employees = [6547: 'Simon', 235345: 'Jack', 4657: 'Jim']
+
+dd(array_reverse(employees))
+// -> [0: "Jim", 1: "Jack", 2: "Simon"]
+
Returns shuffled version of array
+Sorts array by its values. Keys are kept as-is.
+Removes length
amount of values from array
, starting from offset
(if offset
is negative, starts from the end of the array.)
If replacement
is specified, the removed values are replaced with it.
array = ['one', 'two', 'three', 'four', 'five']
+
+dump(array_splice(array, 2, 2))
+// [0: "one", 1: "two", 2: "five"]
+
+dump(array_splice(array, 2, 2, ['hello']))
+// [0: "one", 1: "two", 2: "hello", 3: "five"]
+
Returns the values of an array.
+employees = [6547: 'Simon', 235345: 'Jack', 4657: 'Jim']
+
+dd(array_values(employees))
+
+// -> [0: "Simon", 1: "Jack", 2: "Jim"]
+
Returns array.
+ +Returns value1 && value2
Returns !value
Returns true
if:
""
(empty string)[]
(empty array)null
Returns true
if value is null
. An alternative to if (value == null)
that won't break when value
isn't null due to type checking.
Example:
+ +Casts value
to a bool.
In WebhookScript, dates are not a specific type, but rather expressed as strings that WebhookScript will attempt to parse using a very powerful date parsing engine.
+WebhookScript supports a variety of date formats, and functions taking a date will attempt to guess the format of the input string in order to parse the date into a ISO-8601 format
+If possible, it's recommended to use the ISO-8601 format, for example 2020-05-27T04:00:00.000000Z
.
List of date format characters for the to_date
and date_format
functions.
List of locales/translations available for date display functions
+In addition to automatically recognizing date strings like ISO-8601, WebhookScript has ability to recognize the following special formats.
+now
+4 day
, -2 month
- adds or subtracts to the current date and time, can be suffixed to other datessecond
, minute
, hour
, day
, fortnight
, week
, month
, year
next Thursday
last Monday
first day of January 2008
first Saturday of July 2008
Monday next week
@1215282385
- UNIX timestampFor more information, PHP Supported Date and Time Formats.
+Returns the current date in ISO-8601 format, using timezone
, if specified.
now()
+// -> 2022-12-15T11:00:00.000000Z
+
+now('America/Los_Angeles')
+// -> 2022-12-15T03:00:00.000000-08:00
+
Returns a ISO-8601 formatted date string in UTC time from the provided date
string.
If specified, input_format
is used to parse the date without having to guess the format (see the Date Format Characters specification.) Otherwise, see Recognized date formats.
If the keep_timezone
parameter is set to true, the resulting date string will keep the timezone. The locale
parameter will attempt to parse the date using the specified locale.
If the date is invalid or could not be guessed, null
is returned.
// Current date and time
+'now'.to_date()
+// -> 2020-11-25T00:00:00.000000Z
+
+// Relative formats
+'first monday august 2019'.to_date()
+
+// Automatic format guessing
+'2020-01-01 23:02:01'.to_date()
+
+// Timezone handling
+'2020-01-01 23:02:01'.to_date(null, null, 'GMT-5')
+// -> "2020-01-02T04:02:01.000000Z", interpreted as GMT-5 and converted to UTC
+
+'2020-01-01 23:02:01'.to_date(null, null, 'GMT-5', true)
+// -> "2020-01-01T23:02:01.000000-05:00", date keeps timezone
+
+// Unix timestamp
+'@1215282385'.to_date()
+
+// Custom date format
+'2/4/12 06:03'.to_date('M/D/YY HH:mm')
+// -> 2012-02-04T06:03:00.000000Z
+
+// To escape characters in the format string, backslashes can be used
+'2020-01-05 12h30m15s'.to_date('YYYY-MM-DD HH\\hmm\\mss\\s')
+// -> 2020-01-05T12:30:15.000000Z
+
Returns a date converted to the format specified in format
. date
is automatically parsed and can be a date string (ISO-8601 recommended) or one of the recognized date formats.
For a full list of date format characters, see the Date Format Characters specification.
+If format
is not specified, a default human readable readable string is returned.
date_format('2008-07-05T18:26:25.000000Z', 'YYYY-MM-DD')
+// -> 2008-07-05
+
+date_format('2008-07-05T18:26:25.000000Z', 'LLLL', 'da')
+// -> lørdag d. 5. juli 2008 kl. 18:26
+
+date_format('2020-01-01T23:02:01.000000-05:00', 'LLLL', null, 'GMT+2')
+// -> Thursday, January 2, 2020 6:02 AM
+
+date_format('now', 'x')
+// -> 1606329669220 (current date in UNIX timestamp with microseconds)
+
+// Add 1 hour to an existing date (see Recognized date formats above)
+date_format('2021-10-28 11:28:55 +1 hour', 'YYYY-MM-DD HH:mm:ss')
+// -> 2021-10-28 12:28:55
+
Returns an array containing all the components of a given date.
+dump(date_to_array('2008-07-05T18:26:25.324542Z'))
+
+// [
+// "year": 2008,
+// "month": 7,
+// "day": 5,
+// "dayOfWeek": 6,
+// "dayOfYear": 187,
+// "hour": 18,
+// "minute": 26,
+// "second": 25,
+// "micro": 324542,
+// "timestamp": 1215282385,
+// "formatted": "2008-07-05 18:26:25",
+// "timezone": "Z"
+// ]
+
Calculates the interval between date1
and date2
. When date2
is unspecified/null, now
is used.
If no format string is specified, the interval is returned as the number of seconds between the dates, with the number being negative if date2
is before date1
.
For the format
string, the PHP DateInterval
format specification is used.
date_interval('2008-07-16T23:13:26.234212Z', '2008-07-05T18:26:25.324542Z')
+// -> -967620
+
+date_interval(
+ '2008-07-16T23:13:26.234212Z',
+ '2008-07-05T18:26:25.324542Z',
+ '%d days, %h hours, %i minutes'
+)
+// -> 11 days, 4 hours, 47 minutes
+
Formats the difference between 2 dates in a way that's easy to read for humans.
+If no locale is specified, English is used. When date2
is unspecified/null, now
is used.
date_interval_human(
+ '2008-07-16T23:13:26.234212Z',
+ '2008-07-05T18:26:25.324542Z'
+)
+// -> 1 week after
+
+date_interval_human(
+ '2008-07-16T23:13:26.234212Z',
+ '2008-07-05T18:26:25.324542Z',
+ 'es'
+)
+// -> 1 semana después
+
When you send an email, or send a multipart/form-data request, files are extracted as the file.*.*
variables, but you can also loop over files using WebhookScript.
Returns an array of files, with the following keys:
+dump(files())
+
+// [
+// 0: [
+// "id": "76b7274a-e806-4f72-ba49-85ad05926ef0",
+// "filename": "Screen Shot 2020-06-05 at 2.15.29 PM.png",
+// "size": 1203671,
+// "content_type": "image/png"
+// ]
+// ]
+
+// Filtering files by type
+for (file in files()) {
+ if (r'.*\.png'.match(file['filename'])) {
+ dump(file['filename'] + ' is png.')
+ }
+}
+
+// "Screen Shot 2020-06-05 at 2.15.29 PM.png is png."
+
Returns the content of a specific file, using the id
key from the files()
function above.
Runs Custom Actions in WebhookScript. Can be used to loop over items in a way that not possible using the visual Custom Actions editor.
+The function returns an array of runtime variables which can be used to fetch the result of an action that sets a variable, like google_sheets_get_values
.
The action_type
s and their parameters can be seen here.
The following action extracts a list of files from an array of objects in a JSON array and uploads each file to Dropbox using the dropbox_upload_file
action.
Example
+In this example, we assume that the request content sent to the Webhook.site URL contains the following:
+{
+ "files": [
+ {
+ "url": "https://example.com/path/980e683e-9bd7-4512-bc0e-acfd6e022a81",
+ "name": "example_1.png"
+ },
+ {
+ "url": "https://example.com/path/c8e1282f-46a7-45ab-9699-0472ff9ab96c",
+ "name": "example_2.png"
+ }
+ ]
+}
+
In the script, the request contents are JSON decoded and looped over. For each item, the file's URL is downloaded and a Dropbox Upload File action is executed.
+files = json_path(var('request.content'), '.files.*')
+
+for (file_info in files) {
+
+ // Download file from URL
+ file_download = request(file_info['url'])
+
+ // Generate a destination path for the Dropbox file
+ destination_path = '/Example Files/%s'.format(file_info['name'])
+
+ // Execute Dropbox Upload File action
+ action(
+ 'dropbox_upload_file',
+ [
+ 'path': destination_path,
+ 'body': file_download['content'],
+ 'mode': 'update',
+ 'provider_id': providerId
+ ]
+ )
+
+}
+
Warning
+This function has been deprecated and should no longer be used. As an alternative, it's recommended that you mark a WebhookScript Custom Action as Queued. More info here.
+Executes code in the future. Any output will be stored on the request and will show with a "Was delayed" label.
+The code will not inherit the execution scope.
+In this example, the format
function is used to prepare the code string with a URL, causing {}
to be replaced with with https://example.com
.
code = '
+ request(
+ "{}",
+ \'{"message": "Hello World!"}\',
+ "POST"
+ )
+'
+
+url = 'https://example.com'
+
+delay(5, code.format(url));
+
The maximum amount of seconds allowed is 604800 (7 days).
+Marks the request so it is not saved in Webhook.site, which is useful when receiving a large amount of requests. The request can still be seen when it comes in, but will not be available through through the app later, or through the API. The action is useful in cases where e.g. a URL receive a large amount of requests.
+Executes code in code
and returns the result. The code will inherit the execution scope.
Downloads code located at url
and returns the result. The code will inherit the execution scope.
As an example, this can be used if you want to re-use code. Just upload it to a server or e.g. Github and use it in different WebhookScript actions.
+result = import('https://raw.githubusercontent.com/webhooksite/scripts/ec22946a83ea85f607fcc6bff83f9d81ed2fe4ed/hello_world.ws')
+echo(result) // value
+
Stops Custom Action execution.
+Stops Custom Action execution and return a response immediately.
+Sets or overwrites the response content of the URL. Script execution continues.
+Sets or overwrites a response header of the URL. Script execution continues.
+Sets or overwrites response content, status and headers in single function. Script execution continues.
+headers
should be an array of strings e.g. ["X-Example: Value", "X-Foo: Bar"]
.
Sets or overwrites the HTTP response status of the current URL. Script execution continues.
+ +These are the functions that can be used in your script, and includes various utility functions and functions to interact with your Webhook.site URL.
+Functions can be chained directly to a primitive (strings, numbers, arrays).
+These two statements are equivalent:
+ + +They can even be chained, for example:
+ +Furthermore, functions that begin in a type can be referenced without it, for example, when calling the format
function with the first argument being a string, the language infers that actually the string_format
function should be used.
echo(string_format('hello %s', 'world')) // hello world
+
+'hello %s'.format('world').echo() // hello world
+
Read more about functions in the reference.
+Define your own functions like this:
+ +Read more about functions in the reference.
+Adds string
to script debug output.
Stops Custom Action execution and adds value
to script debug output.
Adds value
as a decoded string to script debug output.
Returns the type name of a value, e.g. "string"
.
Converts a binary tring to a hexadecimal representation.
+Rounds a number up to nearest integer.
+Rounds a number down to nearest integer.
+Returns true
if value
is numeric.
Examples:
+12345
returns true
."1.0"
returns true
.null
returns false
.Returns the remainder after number is divided by divisor
.
Returns the value of Pi.
+Returns a random number between min
and max
.
Rounds a number up with specified precision (number of digits after decimal point.)
+ +Converts an associative array into a form-style string, used for e.g. application/x-www-form-urlencoded
requests or HTTP query strings.
Sends a HTTP request and returns an array with the following keys containing response data:
+content
(contains a cURL error message in case of an error)status
(null
in case of an error)headers
url
The options
array can contain the following keys; none are required.
mode
- string, one of: text (default), multipart, urlencoded, forwardcontent
- string, body content, used when mode is textmethod
- string, HTTP method, e.g. POSTmultipart
- array of arrays, used when mode is multipart, containing keys:name
- required, string, form item namefilename
- stringcontent-type
- stringcontent
- stringurlencoded
- array of arrays, used when mode is urlencoded, containing keys:name
- required, string, form item namevalue
- stringheaders
- array of strings, HTTP headersskip_ssl_verification
- default false, skips TLS/SSL certificiate validation for HTTPS requeststimeout
- number, default 5, timeout in secondsTo send a simple POST request containing e.g. JSON data, use the following:
+http(
+ 'https://example.com',
+ [
+ 'method': 'POST',
+ 'content': '{"example": "json"}',
+ 'headers': ['Content-Type: application/json']
+ ]
+)
+
http(
+ 'https://example.com',
+ [
+ 'method': 'POST',
+ 'mode': 'multipart',
+ 'multipart': [
+ [
+ 'name': 'value1',
+ 'content': 'This is a value',
+ ]
+ ]
+ ]
+)
+
Sends a HTTP request and returns an array with the following keys containing response data:
+content
(contains a cURL error message in case of an error)status
(null
in case of an error)headers
url
The headers should be an array of strings, for example:
+ +To get a JSON document, validate if valid JSON, and get a property:
+response = request('https://example.com')
+
+decoded = json_decode(response['content'])
+if (decoded) {
+ value = decoded['value']
+}
+
If override
is set to true, none of the content from the original request is included (e.g. query strings, headers, content.)
Sends a HTTP Multipart request, e.g. for uploading files.
+name
(the form name value) and content
are required in the items
array; the rest is optional.
timeout
specifies the request timeout in seconds.
The return value is an array with the following keys containing response data:
+content
(contains a cURL error message in case of an error)status
(null
in case of an error)headers
url
Example:
+multipart(
+ 'https://example.com/file-upload', [
+ [
+ 'name': 'file[]',
+ 'filename': 'file1.txt',
+ 'content': 'hello world',
+ 'headers': ['Header1': 'value', 'Header2': 'othervalue']
+ ],
+ [
+ 'name': 'client_id',
+ 'content': 'abcd123',
+ ]
+ ],
+ 'POST',
+ ['Api-Key: xxxx']
+)
+
Returns an URL-decoded version of value.
+Returns an URL-encoded version of value.
+ + +Returns boolean if subject contains value
+Returns position of value in subject, or false if not found
+Returns position of value in subject, or false if not found
+Sprintf-like formatting of formatString with items, see PHP sprintf docs.
+Joins items with string subject
+Returns length of string (multibyte-aware)
+Converts string
to lowercase (multibyte-aware)
Returns number value of string
+Returns a random string of length using the A–Z,a–z,0–9
alphabet.
Replaces string search with replace found in subject.
+string_replace('this is a sentence', 'sentence', 'string') // -> this is a string
+
+string_replace('this is great', r'(.*) is (.*)', '$1 was $2') // -> this was great
+
Replaces items found in subject using the replace_pairs array:
+ +Reverses string subject
+Extracts a segment of a string. Multibyte-aware.
+string_slice('hello world', 0, 5)
returns hello
.
string_slice('hello world', 6)
returns world
.
Returns string where the individual characters has been shuffled.
+Returns array of split string subject with delimiter
+Returns string
converted to title case.
string_title('hello world')
returns Hello World
.
Converts string
to UPPERCASE (multibyte-aware).
Converts a regex string to a regex type
+Returns value as string
+Returns string
with space, tab and newline characters removed from the beginning and end of the string.
Returns a random v4 UUID.
+Takes a CSV string and outputs to an array, with each row being an item in the array.
+content
should be a string containing the CSV document.delimiter
will explicitly set the CSV delimiter the parser will attempt to use (e.g. ;
). Must be a single character. Defaults to ,
(comma.)header_offset
, when specified, causes the output array item's keys to be set to the header values. Setting to 0
will mark the first row as the header row. enclosure
sets the field enclosure character. Must be a single character. Defaults to "
(double quote.)escape
sets the field escape character. Must be a single character. Defaults to \
(backslash.)csv_content = 'firstname,lastname,title
+"M. J.",Plumley,"Sr. Developer"
+Emily,"Jenna Platt","Chief Information Officer"'
+
+array = csv_to_array(csv_content, ',', 0)
+
+echo(json_encode(array))
+
+// [
+// {
+// "firstname": "John",
+// "lastname": "Doe",
+// "title": "Sr. Developer"
+// },
+// {
+// "firstname": "Emily",
+// "lastname": "Jenna Platt",
+// "title": "Chief Information Officer"
+// }
+// ]
+
Returns a base64-decoded string.
+Returns a base64url-decoded string.
+If the base64 string was encoded using URL-friendly base64url
format, this function should be used rather the regular base64_decode
function.
Returns base64-encoded string.
+Returns a URL-friendly base64url-encoded string, where characters +
, /
have been replaced by -
and _
, and any =
padding characters have been removed.
Returns a hashed version of value
using the algo
algorithm.
The following built-in algorithms are available: md2
, md4
, md5
, sha1
, sha224
, sha256
, sha384
, sha512/224
, sha512/256
, sha512
, sha3-224
, sha3-256
, sha3-384
, sha3-512
, ripemd128
, ripemd160
, ripemd256
, ripemd320
, whirlpool
, tiger128,3
, tiger160,3
, tiger192,3
, tiger128,4
, tiger160,4
, tiger192,4
, snefru
, snefru256
, gost
, gost-crypto
, adler32
, crc32
, crc32b
, fnv132
, fnv1a32
, fnv164
, fnv1a64
, joaat
, haval128,3
, haval160,3
, haval192,3
, haval224,3
, haval256,3
, haval128,4
, haval160,4
, haval192,4
, haval224,4
, haval256,4
, haval128,5
, haval160,5
, haval192,5
, haval224,5
, haval256,5
.
Returns the calculated message digest/hash in a hexadecimal formatted as a string.
+For a list of possible values for algo
, see hash()
above.
See also binary and hexadecimal conversion functions.
+In the following example, the hash needs to be converted to binary, and then to base64, to match the signature.
+hmac = hmac(var('request.content'), 'sha256', 'insert_secret_here');
+signature = var('request.header.x-signature', '');
+hash = base64_encode(hex2bin(hmac))
+
+if (hash != signature) {
+ respond('Unauthorized', 401);
+}
+
List of available algorithms for functions in the Cryptography section:
+blake2b512
blake2s256
md4
md5
md5-sha1
ripemd160
sha1
sha224
sha256
sha3-224
sha3-256
sha3-384
sha3-512
sha384
sha512
sha512-224
sha512-256
shake128
shake256
sm3
whirlpool
Signs value
using private_key
and algo
. Returns signature on success, false
on failure.
For an example using sign() for JWT authorization, see here.
+Returns a cryptographic digest of value
using algo
. Returns false
on failure.
Verifies value
using signature
, public_key
and algo
Returns a string with all HTML tags removed.
+html_strip_tags('<b>test</b>')
returns test
.
Converts HTML to plaintext. A more aggressive version of html_strip_tags
.
html_to_text('<b>test</b>')
returns test
.
Decodes all HTML entities (for example,
) to normal characters.
Replaces characters in a string with HTML encoded versions.
+Converts a Markdown string to HTML.
+markdown_to_html('# Hello world')
returns <h1>Hello world</h1>
.
If safe_mode
is set to true, characters like <
, >
are HTML-entity encoded.
Decodes json
and returns an array.
Takes an array and encodes it as a JSON string.
+Per default, the JSON array is formatted with whitespace. To turn it off, set format
to false.
Returns the result of a json
string parsed using the JSONPath functionality.
Per default, if there's just one match (e.g. if matching on a property value that's a string), this value is returned. To always return an array, set return_first
to false.
dump(json_path('{"v": []}', 'v[*]', false))
+dump(json_path('{"v": []}', 'v[*]'))
+// []
+// ""
+
+dump(json_path('{"v": ["item1"]}', 'v[*]', false))
+dump(json_path('{"v": ["item1"]}', 'v[*]'))
+// [0: "item1"]
+// "item1"
+
+dump(json_path('{"v": ["item1", "item2"]}', 'v[*]', false))
+dump(json_path('{"v": ["item1", "item2"]}', 'v[*]'))
+// [0: "item1", 1: "item2"]
+// [0: "item1", 1: "item2"]
+
JSON-escapes all special JSON characters like double quotes, newlines, etc.
+Returns the matching values as an array, or false
if the regex did not match. Supports regex pattern modifiers.
// Returns anything enclosed within <html> tags in a string
+html = preg_match('/<html>(.*)<\\/html>/s', content)
+
Returns the matching string and all match groups as an array, and false
on failure.
input = "You're a good bot"
+
+output = regex_extract(r"You're (\w) (.*)", input)
+
+dump(output) // [0: "You're a good bot", 1: "a", 2: "good bot"]
+
Returns the first match group of a regex, and false
(or default
, if set) on failure.
input = "You're a good bot"
+output = regex_extract(r"You're (.*)", input)
+dump(output) // "a good bot"
+
+input = "Hello world"
+output = regex_extract(r"You're (.*)", input, 'no value')
+dump(output) // "no value"
+
Returns the first matching string, otherwise false.
+input = "You're a good bot"
+
+output = regex_match(r"You're .*", input)
+
+dump(output) // "You're a good bot"
+
Returns the regex converted to a string
+Converts XML to a WebhookScript array. This can be useful for parsing XML, or e.g. converting XML to JSON.
+Note that this function is opinionated - there's no one way to convert XML to an array as XML has more features than WebhookScript arrays. Therefore the function attempts to carry over all the data by automatically generating properties like @value
and @attributes
depending on the input data.
For example, the following XML:
+<orders>
+ <record>
+ <Name type="full">John Doe</Name>
+ <Reference>49690</Reference>
+ </record>
+ <record>
+ <Name type="username">someone@example.com</Name>
+ <Reference>49690</Reference>
+ </record>
+</orders>
+
Converts to JSON via the following script:
+ +Which returns:
+{
+ "orders": {
+ "record": [
+ {
+ "Name": {
+ "@value": "John Doe",
+ "@attributes": {
+ "type": "full"
+ }
+ },
+ "Reference": 49690
+ },
+ {
+ "Name": {
+ "@value": "someone@example.com",
+ "@attributes": {
+ "type": "username"
+ }
+ },
+ "Reference": 49690
+ }
+ ]
+ }
+}
+
Returns the first result of an XPath query on XML document input
.
Given a request with the following content:
+<?xml version="1.0"?>
+<organization name="ExampleCo">
+ <employees>
+ <employee id="1">Jack</employee>
+ <employee id="2">Ann</employee>
+ </employees>
+</organization>
+
xpath(var('$request.content$'), '//employee[1]') // returns "Jack"
var('$request.content$').xpath('//employee[1]') // returns "Jack"
More information and examples regarding XPath.
+Returns the results of an XPath query on XML document input
as an array.
Given a request with the following content:
+<?xml version="1.0"?>
+<organization name="ExampleCo">
+ <employees>
+ <employee id="1">Jack</employee>
+ <employee id="2">Ann</employee>
+ </employees>
+</organization>
+
xpath_all(var('$request.content$'), '//employee]') // returns [0: "Jack", 1: "Ann"]
var('$request.content$').xpath('//employee') // returns [0: "Jack", 1: "Ann"]
More information and examples regarding XPath.
+Performs a "han-kaku" - "zen-kaku" conversion for string string. This function is only useful for Japanese. See here for more info.
+ +These functions lets you interface with other Custom Actions by getting and setting variables from them. These functions are also how you retrieve Global Variables defined in the Control Panel.
+Retrieves the value of a Variable or Global Variable (defined in the Control Panel). The surrounding dollar signs are not mandatory.
+Returns null
(or the value of default
) if the variable does not exist.
var('request.header.x-request-verification') // returns value of the `x-request-verification` header
+
Exports a Variable for use in downstream actions, like the Set Runtime Variable action.
+For example, the following code:
+ +Would cause the string $myvar$
to be replaced with Hello world
in all subsequent actions.
Permanently creates or updates a Global Variable (as defined in Control Panel.)
+The value can also be retrieved with the var()
function in subsequent action executions.
Deletes a global variable.
+A variable (not a function) containing an associative array with all available Webhook.site variables.
+ + +WebhookScript is an easy to use scripting language designed for executing Web-related actions on incoming requests.
+While the other actions like Extract Regex and Send Email allows you to create flows in a visual editor, WebhookScript makes it quicker to create more advanced logic.
+WebhookScript can be combined with other Custom Actions as data can be shared between them using Variables.
+The syntax is very similar to PHP and JavaScript. See also the full language specification.
+products = [
+ 'apple': ['price': 10],
+ 'blueberry': ['price': 1],
+ 'cake': ['price': 550]
+]
+
+shouldAddVat = var('request.query.vat');
+selectedProduct = var('request.query.product');
+
+if (!selectedProduct) {
+ respond('Please select a product!', 500)
+}
+
+price = products[selectedProduct]['price'];
+
+if (shouldAddVat == 1) {
+ price = price * 1.25;
+}
+
+respond(
+ 'Your price is {}'.format(price),
+ 200
+);
+
Custom Action Variables in WebhookScript behave a little differently than other action types: in the code, they will not be replaced automatically like in other action types.
+Instead, to interface with Custom Action Variables (created in previous actions, or default variables provided for each request or email), the function var() can be used.
+The dollar-sign syntax (e.g. $request.content$
) is optional when using the var()
function, and the following two statements are equivalent: var('$request.content$')
/ var('request.content')
.
In addition, set() can be used to export a variable from your script to further downstream actions. store() is used to permanently set a Global Variable.
+The shortcuts are available when the editor is focused.
+Windows | +Mac | +Shortcut | +
---|---|---|
Alt-R | +Alt-R | +Test code (update Debug Panel) | +
Ctrl-S | +Cmd-S | +Save action without exiting | +
Below the editor is the "debug panel" containing data relating to the current and previous actions:
+Debug outputs shows the outputs of all the actions, with the current action being edited or created marked in blue.
+Response shows details of the response of the URL formatted in JSON.
+Variables is a table of all current available variables for use in the script with the var()
function or variables
array.
To enable fullscreen mode, click the Expand button in the upper right corner to make the editor take up more screen space. Click again to disengage fullscreen mode.
+ +These are the functions that can be used in your script, and includes various utility functions and functions to interact with your Webhook.site URL.
+The language is built with a familiar PHP/JS/C-like syntax. Statements are separated using the ;
character, but this is only required for multiple statements on the same line:
Using semicolons (recommended): +
+Using newlines: +
+The language has 6 data types:
+Example usage: +
a = true;
+b = 1 == 2; // false
+c = b == false; // true
+d = c == "hello"; // ERR: Cannot compare 'bool' and 'string'
+e = "hello" == r"[0-9]"; // false
+f = "hello" == r"l{2}"; // true
+
Both integer and float values are supported.
+Example usage:
+ +This data types represents a series of characters. Multi-byte characters (accents, diacritics) are treated properly.
+Example usage: +
+Regex data type exists for advanced string matching. It is defined using the r"..."
literal (a string literal prefixed with r
) and treated as PCRE (Perl-compatible) regular expressions (the same as within PHP itself) with Unicode mode enabled.
Example usage: +
+Arrays are untyped (PHP-style) containers that can accomodate multiple values of different (or same) types. Optionally, array index can be defined for a value. By default, integer index starting from the lowest index found (or from 0
) is used, but strings can also be used.
Example usage: +
a = ["abc", 123, 4: true, false, r"[A-Z]+"];
+// Resulting array: [0: "abc", 1: 123, 4: true, 5: false, 6: r"[A-Z]+"]
+
+b = json_encode(["the_number": 123, "other_numbers": [42, 1337, 80085]])
+// Result:
+// {
+// "the_number": 123,
+// "other_numbers": [
+// 42,
+// 1337,
+// 80085
+// ]
+// }
+
Simple way of defining ranges between numbers is provided via the a..[s..]b
range syntax.
a
is the from value.b
is the to value, inclusive, if it is not "skipped" due to a rather large step (see below).s
(optionally) is the step value which to use.Example usage: +
a = 1..4; // (array) [0: 1, 1: 2, 4: 3, 5: 4]
+b = 1..2..4; // (array) [0: 1, 1: 3]
+c = 10..4..16; // (array) [0: 10, 1: 14]
+_x = 3
+_y = 6
+_z = 2
+d = _x.._z.._y; // (array) [0: 3, 1: 5]
+
array
having values that are based on the range's parameters.
+Function is a value type that represents a "unit" of some self-contained logic. In WebhookScript they have their own type and are treated as first-class citizens: they can be stored inside variables and passed around as such. Direct invocation of an anonymous function is supported, provided that the anonymous function's definition is enclosed in parentheses. A function does capture its surrounding variables.
+Example usage: +
// Traditional definition.
+function sub(a, b) {
+ return a - b;
+}
+
+// A variable "sub" that holds the "sub()" function is now defined in current scope.
+sub(1, 2); // Returns -1
+
+// Storing a function value into a variable.
+// Note: This is equivalent to the previous definition.
+sub_2 = function(a, b) {
+ return a - b;
+};
+
+// A variable "sub_2" that holds the "sub_2()" function is now defined in current scope.
+sub_2(1, 2); // Returns -1
+
+// Creating a function with alternative, short syntax.
+// Note: This is equivalent all of the previous definitions.
+sub_3 = (a, b) => {
+ return a - b;
+};
+
+// A variable "sub_3" that holds the "sub_3()" function is now defined in current scope.
+sub_3(1, 2); // Returns -1
+
+// Creating and using an anonymous function directly.
+// Using an alternative, short syntax.
+((a, b) => {
+ return a - b;
+})(1, 2); // Returns -1
+
In addition to classical function invocation, WebhookScript additionaly supports Uniform Function Call Syntax (UFCS) as a way to call functions "on values". Essentially, it means that calling foo(bar);
is equivalent to calling bar.foo()
, or - to provide an example with additional parameters - that calling foo(bar, 1, true, "something");
is equivalent to calling bar.foo(1, true, "something")
.
When using chained function invocation, the interpreter will try to find the most fitting function to call. "Most fitting" meaning that when the client calls bar()
function on a value having the string
type, WebhookScript will try to find and use the string_bar()
first. If such function is not defined, only then will the interpreter use the original bar()
function.
Consider this a syntactic sugar to make coding in WebhookScript a bit more user-friendly. Because of this the user is able to call "something".length()
on a string the same way as calling [1, 2, 3].length()
on an array, even though there are in fact two separate functions string_length()
and array_length()
invoked behind the scenes.
Plethora of well known operators can be used to define relationships between and/or affect various values. Different operators can have various effects on various data types, some of which are covered down below.
+Precedence of various operators is defined as follows (from highest to lowest):
+- (
... )
(parentheses)
+- !
(logical not)
+- *
, /
(multiplication/division)
+- +
, -
(addition/subtraction)
+- ==
, !=
, >=
, <=
, >
, <
(comparison)
+- and
(logical and)
+- or
(logical not)
+- =
(assignment)
Raw source | +Equivalent to | +
---|---|
1 + 2 + 3 + 4 |
+((1 + 2) + 3) + 4 |
+
1 - 2 + 3 - 4 |
+((1 - 2) + 3) - 4 |
+
1 + 2 * 3 + 4 |
+1 + (2 * 3) + 4 |
+
1 + 2 * 3 / 4 |
+1 + ((2 * 3) / 4) |
+
1 + -2 * 3 / 4 |
+1 + (((-2) * 3) / 4) |
+
1 and 2 or 3 and 4 |
+(1 and 2) or (3 and 4) |
+
1 or 2 or 3 and 4 |
+(1 or 2) or (3 and 4) |
+
1 or !2 or !3 and 4 |
+(1 or (!2)) or ((!3) and 4)) |
+
x = true or false and true |
+x = (true or (false and true)) |
+
x = a == b |
+x = (a == b) |
+
x = a > 5 and b < 6 |
+x = ((a > 5) and (b < 6)) |
+
and
true
if both operands are truthy.false
.or
true
if either one (or both) operand is truthy.false
.!
=
+
, -
+
Add two numbers.-
Subtract two numbers.+
Concatenate two strings.-
Removes all occurences of the right side from the left side.-
(if the right side is a Regex value) Removes all matches of the regex from the left side string.a = 5 + 4 // (number) 9
+b = 5 - 4; // (number) 1
+c = "a word and number " + 5.to_string(); // (string) "a word and number 5"
+d = "a word and number {}".format(5); // (string) "a word and number 5"
+e = "a word" + " and one more"; // (string) "a word and one more"
+f = "a word" - "or"; // (string) "a wd"
+g = "regular expressions" - r"regul[ar]+\s*"; // (string) "expressions"
+_x = 5 + "4" // ERR: Cannot use operator '+' with 'number' and 'string'
+_x = "a word and number " + 5; // ERR: Cannot use operator '+' with 'number' and 'string'
+
*
, /
+
Multiply two numbers.-
Divide two numbers.a = 1 * -2; // (number) -2
+b = 2 * 3; // (number) 6
+c = 2 * "3"; // (string) "33"
+d = "3" * 4; // (string) "3333"
+e = 5 / 4; // (number) 1.25
+g = 5 / 5; // (number) 1
+_x = "2" * "3"; // ERR: Cannot use operator '*' with 'string' and 'string'
+_x = 5 / "4"; // ERR: Cannot use operator '/' with 'number' and 'string'
+_x = "20" / 4; // ERR: Cannot use operator '/' with 'string' and 'number'
+
You can control the flow of your program with several kinds of statements the language provides.
+The if
construct - as in all other programming languages - allows you to dynamically branch your program flow based on some conditions during runtime.
a = false;
+b = true;
+c = 5;
+if (a or b) {
+ d = 1;
+ if (d < c) {
+ e = d + c;
+ }
+}
+// d == 1, e == 6
+
The for
construct allows you to iterate over a value that supports it (array
or string
values), while performing a task on that collection's single item.
txt = "123456789";
+result = [];
+for (n in txt) {
+ if (5 > n.to_number() > 0) {
+ result.push(n);
+ }
+}
+// result == [0: "1", 1: "2", 2: "3", 3: "4"]
+
prices = [100, 200, 300, 600, 1200];
+sentence_template = "This costs {} units of money!";
+results = [];
+for (price in prices) {
+ results.push(sentence_template.format(price));
+}
+// results == [
+// 0: "This costs 100 units of money!",
+// 1: "This costs 200 units of money!",
+// 2: "This costs 300 units of money!",
+// 3: "This costs 600 units of money!",
+// 4: "This costs 1200 units of money!"
+// ]
+
Note: The flow of program inside the for
cycle can be controlled further by using the continue
and break
statements.
The while
construct does a thing if a specified condition is met (if the condition expression has a truthy value).
Note: The flow of program inside the while
cycle can be controlled further by using the continue
and break
statements.