cURL
# Block Web3 Action
curl 'https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/actions/publishFile' \
-H 'X-Access-Key: ${TENDERLY_ACCESS_KEY}' \
-H 'content-type: application/json' \
--data-raw $'{"action":{"name":"block-action","description":"Triggers on every 10th block on ETH Mainnet and Polygon.","source":"// Do not change function name.\\nconst actionFn = async (context, blockEvent) => {\\n console.log(blockEvent)\\n\\n // To access project\'s secret\\n // let secret = await context.secrets.get(\'MY-SECRET\')\\n\\n // To access project\'s storage\\n // let value = await context.storage.getStr(\'MY-KEY\')\\n // await context.storage.putStr(\'MY-KEY\', \'MY-VALUE\')\\n\\n // Your logic goes here :)\\n}\\n// Do not change this.\\nmodule.exports = { actionFn }","triggerType":"BLOCK","runtime":"V2","function":"actionFn","invocationType":"ASYNC","trigger":{"type":"block","block":{"network":["1","137"],"blocks":10}}},"deploy":true}' \
--compressed
# Periodic Web3 Action
curl 'https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/actions/publishFile' \
-H 'X-Access-Key: ${TENDERLY_ACCESS_KEY}' \
-H 'content-type: application/json' \
--data-raw $'{"action":{"name":"periodic-1hour","description":"CRON job triggered every 1 hour","source":"// Do not change function name.\\nconst actionFn = async (context, periodicEvent) => {\\n console.log(periodicEvent)\\n\\n // To access project\'s secret\\n // let secret = await context.secrets.get(\'MY-SECRET\')\\n\\n // To access project\'s storage\\n // let value = await context.storage.getStr(\'MY-KEY\')\\n // await context.storage.putStr(\'MY-KEY\', \'MY-VALUE\')\\n\\n // Your logic goes here :)\\n}\\n// Do not change this.\\nmodule.exports = { actionFn }","triggerType":"PERIODIC","runtime":"V2","function":"actionFn","invocationType":"ASYNC","trigger":{"type":"periodic","periodic":{"cron":"0 * * * *","interval":"1h"}}},"deploy":true}' \
--compressed
# Webhook Web3 Action
curl 'https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/actions/publishFile' \
-H 'X-Access-Key: ${TENDERLY_ACCESS_KEY}' \
-H 'content-type: application/json' \
--data-raw $'{"action":{"name":"webhook-action","description":"Non-authenticated webhook action. You will be able to make a request without using a valid Tenderly token.","source":"// Do not change function name.\\nconst actionFn = async (context, webhookEvent) => {\\n console.log(webhookEvent)\\n\\n // To access project\'s secret\\n // let secret = await context.secrets.get(\'MY-SECRET\')\\n\\n // To access project\'s storage\\n // let value = await context.storage.getStr(\'MY-KEY\')\\n // await context.storage.putStr(\'MY-KEY\', \'MY-VALUE\')\\n\\n // Your logic goes here :)\\n}\\n// Do not change this.\\nmodule.exports = { actionFn }","triggerType":"WEBHOOK","runtime":"V2","function":"actionFn","invocationType":"ASYNC","trigger":{"type":"webhook","webhook":{"authenticated":false}}},"deploy":true}' \
--compressedconst options = {
method: 'POST',
headers: {'X-Access-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
action: {
name: 'block-action',
description: 'Triggers on every 10th block on ETH Mainnet and Polygon.',
source: '// Do not change function name.\nconst actionFn = async (context, blockEvent) => {\n console.log(blockEvent)\n\n // To access project\'s secret\n // let secret = await context.secrets.get(\'MY-SECRET\')\n\n // To access project\'s storage\n // let value = await context.storage.getStr(\'MY-KEY\')\n // await context.storage.putStr(\'MY-KEY\', \'MY-VALUE\')\n\n // Your logic goes here :)\n}\n// Do not change this.\nmodule.exports = { actionFn }',
triggerType: 'BLOCK',
runtime: 'V2',
function: 'actionFn',
invocationType: 'ASYNC',
trigger: {type: 'block', block: {network: ['1', '137'], blocks: 10}}
},
deploy: true
})
};
fetch('https://api.tenderly.co/api/v1/account/{accountSlug}/project/{projectSlug}/actions/publishFile', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.tenderly.co/api/v1/account/{accountSlug}/project/{projectSlug}/actions/publishFile"
payload = {
"action": {
"name": "block-action",
"description": "Triggers on every 10th block on ETH Mainnet and Polygon.",
"source": "// Do not change function name.
const actionFn = async (context, blockEvent) => {
console.log(blockEvent)
// To access project's secret
// let secret = await context.secrets.get('MY-SECRET')
// To access project's storage
// let value = await context.storage.getStr('MY-KEY')
// await context.storage.putStr('MY-KEY', 'MY-VALUE')
// Your logic goes here :)
}
// Do not change this.
module.exports = { actionFn }",
"triggerType": "BLOCK",
"runtime": "V2",
"function": "actionFn",
"invocationType": "ASYNC",
"trigger": {
"type": "block",
"block": {
"network": ["1", "137"],
"blocks": 10
}
}
},
"deploy": True
}
headers = {
"X-Access-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tenderly.co/api/v1/account/{accountSlug}/project/{projectSlug}/actions/publishFile"
payload := strings.NewReader("{\n \"action\": {\n \"name\": \"block-action\",\n \"description\": \"Triggers on every 10th block on ETH Mainnet and Polygon.\",\n \"source\": \"// Do not change function name.\\nconst actionFn = async (context, blockEvent) => {\\n console.log(blockEvent)\\n\\n // To access project's secret\\n // let secret = await context.secrets.get('MY-SECRET')\\n\\n // To access project's storage\\n // let value = await context.storage.getStr('MY-KEY')\\n // await context.storage.putStr('MY-KEY', 'MY-VALUE')\\n\\n // Your logic goes here :)\\n}\\n// Do not change this.\\nmodule.exports = { actionFn }\",\n \"triggerType\": \"BLOCK\",\n \"runtime\": \"V2\",\n \"function\": \"actionFn\",\n \"invocationType\": \"ASYNC\",\n \"trigger\": {\n \"type\": \"block\",\n \"block\": {\n \"network\": [\n \"1\",\n \"137\"\n ],\n \"blocks\": 10\n }\n }\n },\n \"deploy\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Access-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"actions": {
"block-action": {
"id": "349cbb00-64ad-47ec-bbcb-40bffa28559f",
"index": 1,
"actionId": "53712e55-eb77-4ce3-9140-685fd230396a",
"parentActionId": "",
"runtime": "V2",
"function": "implementation:actionFn",
"triggerType": "BLOCK",
"trigger": {
"type": "block",
"block": {
"network": [
"1",
"137"
],
"blocks": 10
}
},
"commitish": null,
"source": "// Do not change function name.\nconst actionFn = async (context, blockEvent) => {\n console.log(blockEvent)\n\n // To access project's secret\n // let secret = await context.secrets.get('MY-SECRET')\n\n // To access project's storage\n // let value = await context.storage.getStr('MY-KEY')\n // await context.storage.putStr('MY-KEY', 'MY-VALUE')\n\n // Your logic goes here :)\n}\n// Do not change this.\nmodule.exports = { actionFn }",
"createdAt": "2023-12-14T18:47:19.26565Z",
"deployRequested": true,
"deployError": null,
"invocationType": "ASYNC"
}
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "bad_request",
"message": "Bad request input parameters"
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "unauthorized",
"message": "Unauthorized"
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "insufficient_permissions",
"message": "Insufficient permissions"
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "resource_not_found",
"message": "The resource you requested could not be found."
}
}{
"error": {
"id": "9a7bbe06-7f6f-4a45-8c78-8d64b9a9d0e1",
"slug": "too_many_requests",
"message": "API Rate limit exceeded."
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "internal_server_error",
"message": "Internal server error"
}
}Actions
Create Web3 Action
Create a new Web3 Action. You can also set the Web3 Action to be deployed once it’s been created.
Validation
When the payload fails validation, the endpoint responds with 400 and the error slug
found_validation_errors without per-field detail. Common causes:
action.namedoesn’t match^[a-zA-Z][a-zA-Z0-9_-]*$(it must start with a letter and may contain only letters, digits, dashes, and underscores; spaces are not allowed). Put human-readable text inaction.descriptioninstead.trigger.periodic.cronis not a standard five-field cron expression (minute, hour, day of month, month, day of week). Seconds fields and@hourly-style shortcuts are not supported.- The function referenced in
action.functionis not present inaction.source. action.triggeris missing for a non-ALERTtrigger type, or the trigger object doesn’t matchtriggerType.action.invocationTypeis not one ofSYNCorASYNC.
POST
/
v1
/
account
/
{accountSlug}
/
project
/
{projectSlug}
/
actions
/
publishFile
cURL
# Block Web3 Action
curl 'https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/actions/publishFile' \
-H 'X-Access-Key: ${TENDERLY_ACCESS_KEY}' \
-H 'content-type: application/json' \
--data-raw $'{"action":{"name":"block-action","description":"Triggers on every 10th block on ETH Mainnet and Polygon.","source":"// Do not change function name.\\nconst actionFn = async (context, blockEvent) => {\\n console.log(blockEvent)\\n\\n // To access project\'s secret\\n // let secret = await context.secrets.get(\'MY-SECRET\')\\n\\n // To access project\'s storage\\n // let value = await context.storage.getStr(\'MY-KEY\')\\n // await context.storage.putStr(\'MY-KEY\', \'MY-VALUE\')\\n\\n // Your logic goes here :)\\n}\\n// Do not change this.\\nmodule.exports = { actionFn }","triggerType":"BLOCK","runtime":"V2","function":"actionFn","invocationType":"ASYNC","trigger":{"type":"block","block":{"network":["1","137"],"blocks":10}}},"deploy":true}' \
--compressed
# Periodic Web3 Action
curl 'https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/actions/publishFile' \
-H 'X-Access-Key: ${TENDERLY_ACCESS_KEY}' \
-H 'content-type: application/json' \
--data-raw $'{"action":{"name":"periodic-1hour","description":"CRON job triggered every 1 hour","source":"// Do not change function name.\\nconst actionFn = async (context, periodicEvent) => {\\n console.log(periodicEvent)\\n\\n // To access project\'s secret\\n // let secret = await context.secrets.get(\'MY-SECRET\')\\n\\n // To access project\'s storage\\n // let value = await context.storage.getStr(\'MY-KEY\')\\n // await context.storage.putStr(\'MY-KEY\', \'MY-VALUE\')\\n\\n // Your logic goes here :)\\n}\\n// Do not change this.\\nmodule.exports = { actionFn }","triggerType":"PERIODIC","runtime":"V2","function":"actionFn","invocationType":"ASYNC","trigger":{"type":"periodic","periodic":{"cron":"0 * * * *","interval":"1h"}}},"deploy":true}' \
--compressed
# Webhook Web3 Action
curl 'https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/actions/publishFile' \
-H 'X-Access-Key: ${TENDERLY_ACCESS_KEY}' \
-H 'content-type: application/json' \
--data-raw $'{"action":{"name":"webhook-action","description":"Non-authenticated webhook action. You will be able to make a request without using a valid Tenderly token.","source":"// Do not change function name.\\nconst actionFn = async (context, webhookEvent) => {\\n console.log(webhookEvent)\\n\\n // To access project\'s secret\\n // let secret = await context.secrets.get(\'MY-SECRET\')\\n\\n // To access project\'s storage\\n // let value = await context.storage.getStr(\'MY-KEY\')\\n // await context.storage.putStr(\'MY-KEY\', \'MY-VALUE\')\\n\\n // Your logic goes here :)\\n}\\n// Do not change this.\\nmodule.exports = { actionFn }","triggerType":"WEBHOOK","runtime":"V2","function":"actionFn","invocationType":"ASYNC","trigger":{"type":"webhook","webhook":{"authenticated":false}}},"deploy":true}' \
--compressedconst options = {
method: 'POST',
headers: {'X-Access-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
action: {
name: 'block-action',
description: 'Triggers on every 10th block on ETH Mainnet and Polygon.',
source: '// Do not change function name.\nconst actionFn = async (context, blockEvent) => {\n console.log(blockEvent)\n\n // To access project\'s secret\n // let secret = await context.secrets.get(\'MY-SECRET\')\n\n // To access project\'s storage\n // let value = await context.storage.getStr(\'MY-KEY\')\n // await context.storage.putStr(\'MY-KEY\', \'MY-VALUE\')\n\n // Your logic goes here :)\n}\n// Do not change this.\nmodule.exports = { actionFn }',
triggerType: 'BLOCK',
runtime: 'V2',
function: 'actionFn',
invocationType: 'ASYNC',
trigger: {type: 'block', block: {network: ['1', '137'], blocks: 10}}
},
deploy: true
})
};
fetch('https://api.tenderly.co/api/v1/account/{accountSlug}/project/{projectSlug}/actions/publishFile', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.tenderly.co/api/v1/account/{accountSlug}/project/{projectSlug}/actions/publishFile"
payload = {
"action": {
"name": "block-action",
"description": "Triggers on every 10th block on ETH Mainnet and Polygon.",
"source": "// Do not change function name.
const actionFn = async (context, blockEvent) => {
console.log(blockEvent)
// To access project's secret
// let secret = await context.secrets.get('MY-SECRET')
// To access project's storage
// let value = await context.storage.getStr('MY-KEY')
// await context.storage.putStr('MY-KEY', 'MY-VALUE')
// Your logic goes here :)
}
// Do not change this.
module.exports = { actionFn }",
"triggerType": "BLOCK",
"runtime": "V2",
"function": "actionFn",
"invocationType": "ASYNC",
"trigger": {
"type": "block",
"block": {
"network": ["1", "137"],
"blocks": 10
}
}
},
"deploy": True
}
headers = {
"X-Access-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.tenderly.co/api/v1/account/{accountSlug}/project/{projectSlug}/actions/publishFile"
payload := strings.NewReader("{\n \"action\": {\n \"name\": \"block-action\",\n \"description\": \"Triggers on every 10th block on ETH Mainnet and Polygon.\",\n \"source\": \"// Do not change function name.\\nconst actionFn = async (context, blockEvent) => {\\n console.log(blockEvent)\\n\\n // To access project's secret\\n // let secret = await context.secrets.get('MY-SECRET')\\n\\n // To access project's storage\\n // let value = await context.storage.getStr('MY-KEY')\\n // await context.storage.putStr('MY-KEY', 'MY-VALUE')\\n\\n // Your logic goes here :)\\n}\\n// Do not change this.\\nmodule.exports = { actionFn }\",\n \"triggerType\": \"BLOCK\",\n \"runtime\": \"V2\",\n \"function\": \"actionFn\",\n \"invocationType\": \"ASYNC\",\n \"trigger\": {\n \"type\": \"block\",\n \"block\": {\n \"network\": [\n \"1\",\n \"137\"\n ],\n \"blocks\": 10\n }\n }\n },\n \"deploy\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Access-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"actions": {
"block-action": {
"id": "349cbb00-64ad-47ec-bbcb-40bffa28559f",
"index": 1,
"actionId": "53712e55-eb77-4ce3-9140-685fd230396a",
"parentActionId": "",
"runtime": "V2",
"function": "implementation:actionFn",
"triggerType": "BLOCK",
"trigger": {
"type": "block",
"block": {
"network": [
"1",
"137"
],
"blocks": 10
}
},
"commitish": null,
"source": "// Do not change function name.\nconst actionFn = async (context, blockEvent) => {\n console.log(blockEvent)\n\n // To access project's secret\n // let secret = await context.secrets.get('MY-SECRET')\n\n // To access project's storage\n // let value = await context.storage.getStr('MY-KEY')\n // await context.storage.putStr('MY-KEY', 'MY-VALUE')\n\n // Your logic goes here :)\n}\n// Do not change this.\nmodule.exports = { actionFn }",
"createdAt": "2023-12-14T18:47:19.26565Z",
"deployRequested": true,
"deployError": null,
"invocationType": "ASYNC"
}
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "bad_request",
"message": "Bad request input parameters"
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "unauthorized",
"message": "Unauthorized"
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "insufficient_permissions",
"message": "Insufficient permissions"
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "resource_not_found",
"message": "The resource you requested could not be found."
}
}{
"error": {
"id": "9a7bbe06-7f6f-4a45-8c78-8d64b9a9d0e1",
"slug": "too_many_requests",
"message": "API Rate limit exceeded."
}
}{
"error": {
"id": "596b1dc7-af60-477b-aab3-6c93eb92ddfa",
"slug": "internal_server_error",
"message": "Internal server error"
}
}Authorizations
ApiKeyAuthBearerAuth
An API key is a token that a client provides when making API calls. Send it as the X-Access-Key request header on any endpoint:
curl '<API_ENDPOINT>' \ -H 'X-Access-Key: ${TENDERLY_ACCESS_KEY}' \ ...
Learn how to generate API access tokens at Tenderly Docs.
Path Parameters
Account slug of the user
Project slug of the account
Body
application/json
Create web3 action payload
A Web3 Action creation payload.
Show child attributes
Show child attributes
Example:
{ "name": "block-action", "description": "Triggers on every 10th block on ETH Mainnet and Polygon.", "source": "// Do not change function name.\nconst actionFn = async (context, blockEvent) => {\n console.log(blockEvent)\n\n // To access project's secret\n // let secret = await context.secrets.get('MY-SECRET')\n\n // To access project's storage\n // let value = await context.storage.getStr('MY-KEY')\n // await context.storage.putStr('MY-KEY', 'MY-VALUE')\n\n // Your logic goes here :)\n}\n// Do not change this.\nmodule.exports = { actionFn }", "triggerType": "BLOCK", "runtime": "V2", "function": "actionFn", "invocationType": "ASYNC", "trigger": { "type": "block", "block": { "network": ["1", "137"], "blocks": 10 } } }
Should Web3 Action be immediately deployed.
Example:
true
Response
A successful response.
- Create Web3 Action Block Response
- Create Web3 Action Periodic Response
- Create Web3 Action Webhook Response
An object with details about created Web3 Action of Block type.
⌘I