In general, to use Clocktick, you probably want to use one of our purpose built SDK's. This allows you to not have to focus on the underlying encryption and (de-)serialisation of jobs and just fire jobs as needed. See the documentation for your specific language to do this.
Generating a encryption key
Internally, Clocktick uses SHA-256 to hash the specified encryption key. This means you can use a key of any size, but it will be hashed to 32 bytes. This is the key you will use to encrypt and decrypt your job data. You should share this across all your endpoints that use Clocktick to ensure you can decrypt the job data if you call from another service in your organisation.
Manually encrypting and decrypting job data without the SDK
To manually encrypt the job data, you should use the following steps:
- When your user instanciates the client, you want to hash the encryption key using SHA-256. This will give you a 32 byte key you can use to encrypt and decrypt the job data using AES-256-GCM.
- You want to serialize all arguments given by the user to the function as a msgpack array. This will be the data you encrypt.
- You want to encrypt the data using AES-256-GCM with the key you hashed earlier. The IV should be a random 12 byte array which is client side generated.
- You want to store the data as
<base64 encoded IV>:<base64 encoded encrypted data>
. You now have your encrypted job data that you can send to the Clocktick API!
To decrypt the job data, you should use the following steps:
- You want to split the data by the
:
character. The first part is the IV and the second part is the encrypted data. If there are not exactly 2 parts, you should raise an error. - Base64 decode the IV and encrypted data.
- Decrypt the data using AES-256-GCM with the key you hashed earlier. The IV should be the IV you base64 decoded earlier.
- Deserialize the data using msgpack. This will give you the arguments the user passed to the function.
Note that if you are using a SDK, you do not need to worry about this. The SDK will automatically handle this for you.
Manually building a client to call the REST API
The API client is a simple REST API. The base URL for all API requests is https://clocktick.dev
and the current version is V1. When you make requests, we need to add the header Authorization: Bearer <token>
where <token>
is your API key from Tokens in your organisation. This is the key you will use to authenticate with the API.
When you handle errors, you will get a error code with the following JSON body:
type
(string): The type of error that occurred.reasons
(string array): The reasons for the error.
If you get a API error, you should handle it gracefully if it has the X-Is-Application-Error: true
header and a 4xx/5xx code. This means the error originated from the API. If it does not have this header, it is a network error and you should raise a different error type to handle it.
The following HTTP routes are available to use and should be implemented by your SDK:
DELETE /api/v1/jobs/:id
: Delete a job by ID. The ID must be a unique ID created within this organisation. Returns a 204 on success.POST /api/v1/jobs
(random ID) orPOST /api/v1/jobs/:id
(specific ID, must be unique): Create a job. The ID must be a unique ID created within this organisation. You should send the following body:start_from
(object): Defines when the job should start. The value type changes depending on whattype
is set to:datetime
: Thedatetime
field should be a string in the formatYYYY-MM-DDTHH:MM:SSZ
.delta
: The rest of the object is a delta object (see below).
All other values in the
type
property are invalid.delta
runs from when the job is created,datetime
runs from the specified time.run_every
(delta object or null): The time until the next event afterstart_from
(and repeating after each event). If null, the job will not repeat.endpoint_id
(string): The ID of the endpoint to send the job to. This should be a unique ID created within this organisation.encrypted_data
(string): The encrypted data to send to the endpoint. This should be encrypted using the steps above.job_type
(string): A unencrypted string shown in both the dashboard and the SDK to identify the job type being performed. This should be related to the specific job being performed.
Delta object
The delta object is used to create a delta from time until the next event. This is generally used when we are adding on top of a dynamic value. The delta object should have the following properties:
years
(uint): The number of years to add.months
(uint): The number of months to add.days
(uint): The number of days to add.hours
(uint): The number of hours to add.minutes
(uint): The number of minutes to add.seconds
(uint): The number of seconds to add.
Manually handling the endpoint callback
When you implement a endpoint callback handler, you first want to verify the request even came from us. When you create the endpoint, you are given a public key. You can use this in combination with the X-Signature-Ed25519
and X-Signature-Timestamp
headers to verify the request came from us. The X-Signature-Timestamp
header is the timestamp of the request in seconds since the Unix epoch. The X-Signature-Ed25519
header is the signature of the request. A quick tip when looking for ED25519 libraries is that Discord uses the same signature format, so you can use their libraries (or third party libraries for their platform) to verify the signature.
From here, the body will be a JSON body with the following properties:
type
(string): The type of job that was sent.encrypted_data
(string): The encrypted data that was sent. This should be decrypted using the steps above and then be given to the function that should be run.
To mark the job as successful, the server should return a 2xx status code. To mark the job as failed, any other status code should be returned. If the job failed, the job will be retried 20 times (once per minute) before being abandoned.
Note that if you are using a SDK, you do not need to worry about this. The SDK will automatically handle this for you when you have mounted the endpoint in your application.