NAV
shell

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 \
  '{
     "group": null | group_id : uid | group name : string,
     "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
group Optional provide group's cronhooks id or group name. If name is provided and it doesn't exists then it will be created on the fly.
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 \
  '{
     "group": null | gorup_id : uid | group name : string,
     "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
group Optional provide group's cronhooks id or group name.
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:

{
  "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.

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

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.

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.

  1. Go to the "Alerts" page from top right menu of "User Name" and there you will see "Add to Slack" button. Click on it. Cronhooks Slack Integration

  2. "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). Cronhooks Slack Authorization

  3. 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);

Environment Variables

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.

System Variables

These variables can be directly used in your schedules.

  1. _CRONHOOK_ID - Id of schedule
  2. _RANDOM - a random number
  3. _UUID - a random guid
  4. _TIMESTAMP - unix timestamp (seconds)

Escape Braces

If you need to escape curly braces you can use this notation:

{%{ {{ escaped_string }} }%} // output => {{ escaped_string }}

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.