Introduction
Welcome to the Cronhooks API! You can use our API to access Cronhooks API endpoints, which can get information on your scheduled webhooks.
Schedules
Schedule a new webhook
curl https://api.cronhooks.io/schedules /
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_API_TOKEN>' \
-d \
'{
"groupId": null | group_id,
"title": "Send email to inactive users",
"description": "Send an email to users who are inactive for more than 60 days and offer a discount for upgrading subscriptions.",
"url": "https://example.com/webhooks/inactive-email",
"timezone": "asia/karachi",
"method": "POST",
"headers": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"payload": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"contentType": "application/json; charset=utf-8",
"isRecurring": false,
"cronExpression": "",
"runAt": "2019-02-01T12:48:26.983", // if isRecurring => false
"sendCronhookObject": true,
"sendFailureAlert": true,
"startsAt": "2023-02-01T13:00:00", // if isRecurring => true
"endsAt": "2023-03-01T13:00:00" // if isRecurring => true
"retryCount": "3", // if isRecurring => false
"retryIntervalSeconds": "5" // if isRecurring => false
}'
The above command returns JSON structured like this:
{
"id": "3a012d1f-fe83-4855-2860-84f8a85e154c",
"groupId": "3a0136ac-123f-c222-5b2d-ae5b0c5937a6",
"title": "Send email to inactive users",
"description": "Send an email to users who are inactive for more than 60 days and offer a discount for upgrading subscriptions.",
"url": "https://example.com/webhooks/inactive-email",
"timezone": "asia/karachi",
"method": "POST",
"headers": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"payload": "{
\"additionalProp1\": \"string\",
\"additionalProp2\": \"string\",
\"additionalProp3\": \"string\"
}",
"contentType": "application/json; charset=utf-8",
"status": "scheduled",
"errorMessage": "",
"isRecurring": false,
"cronExpression": "",
"runAt": "2019-02-01T12:48:26.983",
"sendCronhookObject": true,
"sendFailureAlert": true,
"startsAt": "2023-02-01T13:00:00",
"endsAt": "2023-03-01T13:00:00",
"retryCount": "3",
"retryIntervalSeconds": "5",
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
This endpoint creates a new scheduled webhook.
HTTP Request
POST https://api.cronhooks.io/schedules
Parameters
Parameter | Required | Description |
---|---|---|
groupId | Optional |
provide group's id |
title | Required |
title of webhook schedule |
url | Required |
webhook url |
timezone | Required |
IANA Timezone |
method | Required |
Http METHOD |
headers | Optional |
{} Object |
payload | Optional |
{} Object or "" String |
contentType | Required |
Content Type for recieving webhook |
isRecurring | Required |
Trigger webhook repeatedly or not |
runAt | Required |
if isRecurring => false, Webhook will be triggered at given DateTime |
sendCronhookObject | Required |
Send Cronhook schedule object along with provided payload |
sendFailureAlert | Required |
If false no alerts will be sent on any channel |
cronExpression | Required |
if isRecurring => true, Valid cron expression for recurring schedules |
startsAt | Optional |
if isRecurring => true, Webhook will start at given DateTime |
endsAt | Optional |
if isRecurring => true, Webhook will end at given DateTime |
retryCount | Optional |
if isRecurring => false |
retryIntervalSeconds | Optional |
if isRecurring => false |
List Schedules
curl "https://api.cronhooks.io/schedules?skip=0&limit=10"
-H "Authorization: Bearer <YOUR_API_KEY>"
The above command returns JSON structured like this:
{
"totalCount": 0,
"items": [
{
"id": "3a012d1f-fe83-4855-2860-84f8a85e154c",
"groupId": "3a0136ac-123f-c222-5b2d-ae5b0c5937a6",
"title": "Send email to inactive users",
"description": "Send an email to users who are inactive for more than 60 days and offer a discount for upgrading subscriptions.",
"url": "https://example.com/webhooks/inactive-email",
"timezone": "asia/karachi",
"method": "POST",
"headers": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"payload": "{
\"additionalProp1\": \"string\",
\"additionalProp2\": \"string\",
\"additionalProp3\": \"string\"
}",
"contentType": "application/json; charset=utf-8",
"status": "scheduled",
"errorMessage": "",
"isRecurring": false,
"cronExpression": "",
"runAt": "2019-02-01T12:48:26.983",
"sendCronhookObject": true,
"sendFailureAlert": true,
"startsAt": "2023-02-01T13:00:00",
"endsAt": "2023-03-01T13:00:00",
"retryCount": "3",
"retryIntervalSeconds": "5",
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
]
}
This endpoint retrieves all scheduled webhooks.
HTTP Request
GET https://api.cronhooks.io/schedules?skip=0&limit=10
Query Parameters
Parameter | Required | Description |
---|---|---|
skip | Required |
Number of records to skip. |
limit | Required |
Number of records to return in response. |
Retrieve a Scheduled Webhook
curl "https://api.cronhooks.io/schedules/3a012d1f-fe83-4855-2860-84f8a85e154c"
-H "Authorization: Bearer <YOUR_API_TOKEN>"
The above command returns JSON structured like this:
{
"id": "3a012d1f-fe83-4855-2860-84f8a85e154c",
"groupId": "3a0136ac-123f-c222-5b2d-ae5b0c5937a6",
"title": "Send email to inactive users",
"description": "Send an email to users who are inactive for more than 60 days and offer a discount for upgrading subscriptions.",
"url": "https://example.com/webhooks/inactive-email",
"timezone": "asia/karachi",
"method": "POST",
"headers": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"payload": "{
\"additionalProp1\": \"string\",
\"additionalProp2\": \"string\",
\"additionalProp3\": \"string\"
}",
"contentType": "application/json; charset=utf-8",
"status": "scheduled",
"errorMessage": "",
"isRecurring": false,
"cronExpression": "",
"runAt": "2019-02-01T12:48:26.983",
"sendCronhookObject": true,
"sendFailureAlert": true,
"startsAt": "2023-02-01T13:00:00",
"endsAt": "2023-03-01T13:00:00",
"retryCount": "3",
"retryIntervalSeconds": "5",
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
This endpoint retrieves a specific schedule.
HTTP Request
GET https://api.cronhooks.io/schedules/{id}
URL Parameters
Parameter | Required | Description |
---|---|---|
id | Required |
The ID of the schedule to retrieve |
Update Scheduled Webhook
curl "https://api.cronhooks.io/schedules/3a0136ac-123f-c222-5b2d-ae5b0c5937a6"
-X PUT \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_API_TOKEN>' \
-d \
'{
"groupId": null | group_id,
"title": "Send email to inactive users",
"description": "Send an email to users who are inactive for more than 60 days and offer a discount for upgrading subscriptions.",
"url": "https://example.com/webhooks/inactive-email",
"timezone": "asia/karachi",
"method": "POST",
"headers": {
"additionalProp1": "string"
},
"payload": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string",
"additionalProp4": "string"
},
"contentType": "application/json; charset=utf-8",
"isRecurring": true,
"cronExpression": "*/5 * * * *",
"sendCronhookObject": true,
"sendFailureAlert": true,
"retryCount": "1",
"retryIntervalSeconds": "5",
"startsAt": "2023-02-01T13:00:00",
"endsAt": "2023-03-01T13:00:00"
}'
The above command returns JSON structured like this:
{
"id": "3a012d1f-fe83-4855-2860-84f8a85e154c",
"groupId": "3a0136ac-123f-c222-5b2d-ae5b0c5937a6",
"title": "Send email to inactive users",
"description": "Send an email to users who are inactive for more than 60 days and offer a discount for upgrading subscriptions.",
"url": "https://example.com/webhooks/inactive-email",
"timezone": "asia/karachi",
"method": "POST",
"headers": {
"additionalProp1": "string"
},
"payload": "{
\"additionalProp1\": \"string\",
\"additionalProp2\": \"string\",
\"additionalProp3\": \"string\"
}",
"contentType": "application/json; charset=utf-8",
"isRecurring": true,
"cronExpression": "*/5 * * * *",
"nextRunAt": "2019-02-01T12:48:26.983",
"sendCronhookObject": true,
"sendFailureAlert": true,
"startsAt": "2023-02-01T13:00:00",
"endsAt": "2023-03-01T13:00:00",
"retryCount": "1",
"retryIntervalSeconds": "5",
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
This endpoint updates a scheduled webhook.
HTTP Request
PUT https://api.cronhooks.io/schedules/{id}
Parameters
Parameter | Required | Description |
---|---|---|
groupId | Optional |
provide group's id |
id | Required |
id of updated schedule |
title | Required |
title of webhook schedule |
url | Required |
webhook url |
timezone | Required |
IANA Timezone |
method | Required |
Http METHOD |
headers | Optional |
{} Object |
payload | Optional |
{} Object or "" String |
contentType | Required |
Content Type for recieving webhook |
isRecurring | Required |
Trigger webhook repeatedly or not |
runAt | Required |
if isRecurring => false, Webhook will be triggered at given DateTime |
cronExpression | Required |
if isRecurring => true, Valid cron expression for recurring schedules |
sendCronhookObject | Required |
Send Cronhook schedule object along with provided payload |
sendFailureAlert | Required |
If false no alerts will be sent on any channel |
startsAt | Optional |
if isRecurring => true, Webhook will start at given DateTime |
endsAt | Optional |
if isRecurring => true, Webhook will end at given DateTime |
retryCount | Optional |
if isRecurring => false |
retryIntervalSeconds | Optional |
if isRecurring => false |
Trigger Webhook
curl "https://api.cronhooks.io/schedules/3a012d1f-fe83-4855-2860-84f8a85e154c/trigger"
-X POST
-H "Authorization: Bearer <YOUR_API_TOKEN>"
The above command returns 204 for success and for error JSON structured like this:
{
"error": {
"code": null,
"message": "error message",
"details": null,
"data": null,
"validationErrors": null
}
}
This endpoint triggers a specific webhook immediately. It does not affect the schedule.
HTTP Request
POST https://api.cronhooks.io/schedules/{id}/trigger
URL Parameters
Parameter | Required | Description |
---|---|---|
id | Required |
The ID of the schedule to trigger |
Pause Schedule
curl "https://api.cronhooks.io/schedules/3a012d1f-fe83-4855-2860-84f8a85e154c/pause"
-X POST
-H "Authorization: Bearer <YOUR_API_TOKEN>"
The above command returns 204 for success and for error JSON structured like this:
{
"error": {
"code": null,
"message": "error message",
"details": null,
"data": null,
"validationErrors": null
}
}
This endpoint pause's given schedule.
HTTP Request
POST https://api.cronhooks.io/schedules/{id}/pause
URL Parameters
Parameter | Required | Description |
---|---|---|
id | Required |
The ID of the schedule |
Resume Schedule
curl "https://api.cronhooks.io/schedules/3a012d1f-fe83-4855-2860-84f8a85e154c/resume"
-X POST
-H "Authorization: Bearer <YOUR_API_TOKEN>"
The above command returns 204 for success and for error JSON structured like this:
{
"error": {
"code": null,
"message": "error message",
"details": null,
"data": null,
"validationErrors": null
}
}
This endpoint resume's given schedule.
HTTP Request
POST https://api.cronhooks.io/schedules/{id}/resume
URL Parameters
Parameter | Required | Description |
---|---|---|
id | Required |
The ID of the schedule |
Delete Scheduled Webhook
curl "https://api.cronhooks.io/schedules/3a012d1f-fe83-4855-2860-84f8a85e154c"
-X DELETE
-H "Authorization: Bearer <YOUR_API_TOKEN>"
The above command returns 204 for success and for error JSON structured like this:
{
"error": {
"code": null,
"message": "error message",
"details": null,
"data": null,
"validationErrors": null
}
}
This endpoint deletes a specific schedule.
HTTP Request
DELETE https://api.cronhooks.io/schedules/{id}
URL Parameters
Parameter | Required | Description |
---|---|---|
id | Required |
The ID of the schedule to delete |
Groups
List Groups
curl "https://api.cronhooks.io/groups?skip=0&limit=10"
-H "Authorization: Bearer <YOUR_API_KEY>"
The above command returns JSON structured like this:
{
"totalCount": 2,
"items": [
{
"id": "3a012d1f-fe83-4855-2860-84f8a85e154c",
"name": "Group #1",
"environmentVariables": [{
"key": "BASE_URL",
"value": " https://example.com",
"protected": false
}],
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
},
{
"id": "3a013346-7e3d-eaa5-feee-371f5f8614ad",
"name": "Group #2",
"environmentVariables": [],
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
]
}
This endpoint retrieves all groups.
HTTP Request
GET https://api.cronhooks.io/groups?skip=0&limit=10
Query Parameters
Parameter | Required | Description |
---|---|---|
skip | Required |
Number of records to skip. |
limit | Required |
Number of records to return in response. |
Retrieve Group by Id
curl "https://api.cronhooks.io/groups/3a013346-7e3d-eaa5-feee-371f5f8614ad"
-H "Authorization: Bearer <YOUR_API_TOKEN>"
The above command returns JSON structured like this:
{
"id": "3a013346-7e3d-eaa5-feee-371f5f8614ad",
"name": "Group #1",
"environmentVariables": [{
"key": "BASE_URL",
"value": " https://example.com",
"protected": false
}],
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
This endpoint retrieves a specific group.
HTTP Request
GET https://api.cronhooks.io/groups/{id}
URL Parameters
Parameter | Required | Description |
---|---|---|
id | Required |
Group Id |
Retrieve Group by Name
curl "https://api.cronhooks.io/groups/getByName?name=Group%20%231"
-H "Authorization: Bearer <YOUR_API_TOKEN>"
The above command returns JSON structured like this if group exists:
{
"id": "3a013346-7e3d-eaa5-feee-371f5f8614ad",
"name": "Group #1",
"environmentVariables": [{
"key": "BASE_URL",
"value": " https://example.com",
"protected": false
}],
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
If group doesn't exist:
{
"error": {
"code": null,
"message": "There is no entity Group with name = Group #1!",
"details": null,
"data": null,
"validationErrors": null
}
}
This endpoint retrieves a specific group by name.
HTTP Request
GET https://api.cronhooks.io/groups/getByName?name=Group%20%231
URL Parameters
Parameter | Required | Description |
---|---|---|
name | Required |
The url encoded (encodeURIComponent) group name |
Retrieve or Create Group with Name
curl "https://api.cronhooks.io/groups/name"
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_API_TOKEN>' \
-d \
'{
"name": "Group #1",
"environmentVariables": [{"key": "BASE_URL", "value": "https://example.com", protected: false}]
}'
The above command always returns JSON structured like this:
{
"id": "3a013346-7e3d-eaa5-feee-371f5f8614ad",
"name": "Group #1",
"environmentVariables": [{
"key": "BASE_URL",
"value": " https://example.com",
"protected": false
}],
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
This endpoint retrieves a specific group by name. If group doesn't exist then it will be created on the fly.
HTTP Request
`POST https://api.cronhooks.io/groups/name
URL Parameters
Parameter | Required | Description |
---|---|---|
name | Required |
The url encoded (encodeURIComponent) group name |
environmentVariables | Optional |
group variables. |
Create Group
curl "https://api.cronhooks.io/groups"
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_API_TOKEN>' \
-d \
'{
"name": "Group #1",
"environmentVariables": [{"key": "BASE_URL", "value": "https://example.com", protected: false}]
}'
The above command returns JSON structured like this:
{
"id": "3a013346-7e3d-eaa5-feee-371f5f8614ad",
"name": "Group #1",
"environmentVariables": [{
"key": "BASE_URL",
"value": " https://example.com",
"protected": false
}],
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
This endpoint creates a group.
HTTP Request
POST https://api.cronhooks.io/groups
Parameters
Parameter | Required | Description |
---|---|---|
name | Required |
group name. |
environmentVariables | Optional |
group variables. |
Update Group
curl "https://api.cronhooks.io/groups/3a013346-7e3d-eaa5-feee-371f5f8614ad"
-X PUT \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_API_TOKEN>' \
-d \
'{
"name": "Group #2",
"environmentVariables": [{"key": "BASE_URL", "value": "https://example.com", protected: false}]
}'
The above command returns JSON structured like this:
{
"id": "3a013346-7e3d-eaa5-feee-371f5f8614ad",
"name": "Group #2",
"environmentVariables": [{
"key": "BASE_URL",
"value": " https://example.com",
"protected": false
}],
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
This endpoint updates a group.
HTTP Request
PUT https://api.cronhooks.io/groups/{id}
Parameters
Parameter | Required | Description |
---|---|---|
id | Required |
group id. |
name | Required |
group name. |
environmentVariables | Optional |
group variables. |
Delete Group
curl "https://api.cronhooks.io/groups/3a013346-7e3d-eaa5-feee-371f5f8614ad"
-X DELETE
-H "Authorization: Bearer <YOUR_API_TOKEN>"
The above command returns 204 for success and for error JSON structured like this:
{
"error": {
"code": null,
"message": "error message",
"details": null,
"data": null,
"validationErrors": null
}
}
This endpoint deletes a specific group. All groups schedules will also be deleted.
HTTP Request
DELETE https://api.cronhooks.io/groups/{id}
URL Parameters
Parameter | Required | Description |
---|---|---|
id | Required |
The ID of the group to delete |
List Group Schedules
curl "https://api.cronhooks.io/groups/{groupId}/schedules?skip=0&limit=10"
-H "Authorization: Bearer <YOUR_API_KEY>"
The above command returns JSON structured like this:
{
"totalCount": 2,
"items": [
{
"id": "3a013346-7e3d-eaa5-feee-371f5f8614ad",
"groupId": "3a012d1f-fe83-4855-2860-84f8a85e154c",
"title": "Send email to inactive users",
"description": "Send an email to users who are inactive for more than 60 days and offer a discount for upgrading subscriptions.",
"url": "{{BASE_URL}}/webhooks/inactive-email",
"timezone": "asia/karachi",
"method": "POST",
"headers": {
"additionalProp1": "string"
},
"payload": "{}",
"contentType": "application/json; charset=utf-8",
"isRecurring": true,
"cronExpression": "*/5 * * * *",
"nextRunAt": "2019-02-01T12:48:26.983",
"sendCronhookObject": true,
"sendFailureAlert": true,
"startsAt": "2023-02-01T13:00:00",
"endsAt": "2023-03-01T13:00:00",
"retryCount": "1",
"retryIntervalSeconds": "5",
"creationTime": "2019-02-01T09:35:27.568Z",
"lastModificationTime": "2019-02-01T09:35:27.568Z"
}
]
}
This endpoint retrieves all schedules for a given group.
HTTP Request
GET https://api.cronhooks.io/groups/{groupId}/schedules?skip=0&limit=10
Query Parameters
Parameter | Required | Description |
---|---|---|
groupId | Required |
group id |
skip | Required |
Number of records to skip. |
limit | Required |
Number of records to return in response. |
Environment Variables (Global)
Group & global environment variables can be used in webhook URLs, headers and payload. Use {{CUSTOM_VAR}} notation. Group level variables override global variables, if they have same name. Variables are case sensitive.
List Environment Variables
curl "https://api.cronhooks.io/environment-variables"
-H "Authorization: Bearer <YOUR_API_KEY>"
The above command returns JSON structured like this:
{
"items": [
{
"key": "BASE_URL",
"value": "https://api.cronhooks.io",
"protected": false
},
{
"key": "API_KEY",
"value": "key_b1c4553be9dab17879b4a2ba2c5aecc2c13cafef",
"protected": true
}
]
}
This endpoint retrieves all environment variables.
Update Environment Variables
curl "https://api.cronhooks.io/environment-variables"
-X PUT \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <YOUR_API_TOKEN>' \
-d \
'{
overwrite: false,
environmentVariables: [
{
"key": "API_SECRET",
"value": "sec_b1c4553be9dab17879b4a2ba2c5aecc2c13cafef",
"protected": true
}
]
}'
The above command returns JSON structured like this:
{
"items": [
{
"key": "BASE_URL",
"value": "https://api.cronhooks.io",
"protected": false
},
{
"key": "API_KEY",
"value": "key_b1c4553be9dab17879b4a2ba2c5aecc2c13cafef",
"protected": true
},
{
"key": "API_SECRET",
"value": "sec_b1c4553be9dab17879b4a2ba2c5aecc2c13cafef",
"protected": true
}
]
}
This endpoint environment variables.
HTTP Request
PUT https://api.cronhooks.io/environment-variables
Parameters
Parameter | Required | Description |
---|---|---|
overwrite | Required |
Overwrite existing variables. |
environmentVariables | Required |
Variables. |
overwrite: true
will remove all existing variables and update with new variables.
overwrite: false
will append new variables and update with new value if a variable already exists.
HTTP Request
PUT https://api.cronhooks.io/environment-variables
System Variables
These variables can be directly used in your schedules.
- _CRONHOOK_ID - Id of schedule
- _RANDOM - a random number
- _UUID - a random guid
- _TIMESTAMP - unix timestamp (seconds)
Escape Braces
If you need to escape curly braces you can use this notation:
{%{ {{ escaped_string }} }%} // output => {{ escaped_string }}
Notifications Integrations
Webhook
You will recieve schedule failure payload as follows
{
"action": "webhook.failed",
"webhook":
{
...
}
}
A POST request will be sent to your provided webhook in case of failure if your shceduled webhook does not return 2xx status code.
Slack
In order to integrate Slack with your cronhooks account, you should follow these steps.
Go to the "Alerts" page from top right menu of "User Name" and there you will see "Add to Slack" button. Click on it.
"Add to Slack" button will redirect you to the Slack auth page where you can choose the channel you want to get Cronhooks alerts to (see the screenshot below).
After choosing the channel "Authorize" it and you will be redirected back to Cronhooks alerts page. Here you can see all your Slack channels in the list. You can add & delete as many channels as you like.
You can read our privacy policy here.
Webhook Signature Verification
To verify the the authenticity that your scheduled webhook was triggered by cronhooks you can check the signature.
ASP.NET / C#
[Route("api/[controller]")]
public class CronhooksFailureWebHook : Controller
{
// You can find your endpoint's secret in your API Keys settings
const string secret = "wh_...";
[HttpPost]
public void Index()
{
var json = new StreamReader(HttpContext.Request.Body).ReadToEnd();
var computedSignature = ComputeSignature(secret, json);
var cronhooksSignature = Request.Headers["Cronhooks-Signature"];
if (SecureCompare(computedSignature, cronhooksSignature))
{
// Verified
}
}
private string ComputeSignature(string secret, string payload)
{
var secretBytes = Encoding.UTF8.GetBytes(secret);
var payloadBytes = Encoding.UTF8.GetBytes(payload);
using (var cryptographer = new HMACSHA256(secretBytes))
{
var hash = cryptographer.ComputeHash(payloadBytes);
return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
}
}
private bool SecureCompare(string a, string b)
{
if (a.Length != b.Length)
{
return false;
}
var result = 0;
for (var i = 0; i < a.Length; i++)
{
result |= a[i] ^ b[i];
}
return result == 0;
}
}
PHP
$webhook_secret = 'wh_...';
$payload = @file_get_contents('php://input');
$cronhooks_sig_header = $_SERVER['HTTP_CRONHOOKS_SIGNATURE'];
$signature = computeSignature($payload, $webhook_secret);
if(secureCompare($cronhooks_sig_header, $signature)){
// Verified
}
private function computeSignature($payload, $secret){
return hash_hmac("sha256", $payload, $secret);
}
private function secureCompare($secret, $computed){
if(function_exists('hash_equals')){
return hash_equals($secret, $computed);
}
if (strlen($secret) != strlen($computed)) {
return false;
}
$result = 0;
for ($i = 0; $i < strlen($secret); $i++) {
$result |= ord($secret[$i]) ^ ord($computed[$i]);
}
return ($result == 0);
}
Typescript / Node
import crypto from 'crypto'
const cronhooksSignature = req.headers['cronhooks-signature']; // req.headers['Cronhooks-Signature']
let body = rawBody.toString();
const expectedSignature = crypto
.createHmac('sha256', process.env.CRONHOOKS_SECRET)
.update(body)
.digest('hex');
let isValidRequest = (cronhooksSignature !== expectedSignature);
Errors
The Cronhooks API uses the following error codes:
Error Code | Meaning |
---|---|
400 | Bad Request -- Your request is invalid. |
401 | Unauthorized -- Your API key is wrong. |
404 | Not Found -- The specified resource could not be found. |
405 | Method Not Allowed -- You tried to access a resource with an invalid method. |
406 | Not Acceptable -- You requested a format that isn't json. |
429 | Too Many Requests -- You're accessing API with too many requests |
500 | Internal Server Error -- We had a problem with our server. Try again later. |
503 | Service Unavailable -- We're temporarily offline for maintenance. Please try again later. |