Webhooks
Time Stream can send webhooks to external services when certain events occur.
About Time Stream webhooks
Webhooks are a way to notify external services when certain events occur in Time Stream.
In keeping with the principles of privacy and control, Time Stream does not store or send your data to any external services unless you choose to do so by setting up a webhook endpoint.
When sending webhooks, Time Stream does so right from your browser. Your data is not stored on or sent to our servers in any way; the webhook is sent directly from your browser to the webhook URL you provide.
Supported events
Time Stream will send webhooks when the following events occur:
Entry events
- Entry created
- Entry updated
- Entry deleted
Backup events
- Backup created
You must select at least one event type when configuring a webhook.
Setting up a webhook endpoint
Before Time Stream will send data to an external service, you need to setup a webhook endpoint. This information will be used to send a webhook each time a relevant event occurs in Time Stream.
Follow these steps to set up a webhook endpoint:
- Click on the cog icon in the top right corner of the app.
- Choose Manage webhooks.
- Click the Create button.
- Provide a URL for the webhook and configure the remaining options.
- Click the Save button.
Webhook endpoint options
When creating a webhook endpoint, you can configure the following options.
Name
You can provide a descriptive name for your webhook to help you identify it. This is optional but recommended for easier management.
URL
The URL where webhook requests will be sent. This is required and should be a valid HTTPS URL.
Webhook body content
This setting allows you to control how the webhook payload is formatted. You can choose the following options:
- JSON - The webhook content will be sent as a JSON object.
- Signed JSON (JWS) - The webhook content will be signed with a secret key using JSON Web Signature (JWS).
- Encrypted JSON (JWE) - The webhook content will be encrypted with a secret key using JSON Web Encryption (JWE).
- Signed and Encrypted JSON (JWS+JWE) - The webhook content will first be signed using JWS, then encrypted using JWE.
When you create a webhook endpoint, Time Stream automatically generates unique secrets that are used to sign and/or encrypt the webhook payloads based on your selected format.
The secrets can be used to verify and decrypt incoming webhooks:
- Signing secret - Used to verify the signature of webhooks sent with JWS signing to ensure authenticity.
- Encryption secret - Used to decrypt webhooks sent with JWE encryption to access the payload.
Time Stream uses the following formats:
- JWS uses compact serialization with
HS256
algorithm (HMAC with SHA-256). - JWE uses compact serialization with
A256KW
key wrapping andA256GCM
content encryption.
Include headers
If you enable this option, Time Stream will set the Content-Type
header to one of the following:
application/json
- If you chooseJSON
for the webhook content.application/jose
- If you choose any of the signing or encryption options (JWS/JWE) for the webhook content.
If you do not enable this option, Time Stream will not set the Content-Type
header, but it will be defaulted to text/plain
by your browser.
Method
The method to use when sending the webhook. The default is POST, but you can also choose PUT if your webhook endpoint requires it.
Enabled
You can enable or disable the webhook. When disabled, Time Stream will not make any requests to the URL provided.
Event types
When creating a webhook, you must select which event types will trigger the webhook. This allows you to send webhooks to different services for different types of activities.
Event selections are organized into two categories:
- Entry events - related to time entries (created, updated, deleted)
- Backup events - related to backup operations (created)
Tag filters
If you've selected any entry-related events, you can also filter by tags. This allows you to send webhooks only for entries with specific tags.
If you don't configure a tag filter, all entries will be considered for the selected entry-related event types.
Tags
The tags to filter by. You can choose one or more tags from your existing tag collection. Only entries that match these tags will trigger webhooks.
Tags filter type
The type of filter to apply to the tags. You can choose between:
- Any - Will send the webhook if the entry has any of the chosen tags (more inclusive).
- All - Will send the webhook only if the entry has all of the tags you have chosen (more restrictive).
Webhooks
Webhooks are generated for each event that occurs in Time Stream. Once generated, the webhook is sent to the webhook endpoint URL you provided.
Webhook payload
The webhook payload is the data that will be sent to the webhook URL.
The payload will be a JSON object containing the event data and the related entry (found within the data
property).
1{2 "id": "A7E1331A63BB0D69",3 "createdAt": "2025-02-24T10:54:39.434Z",4 "object": "event",5 "data": {6 "id": "d74c4f01-b9bd-44b2-b647-bfdaf2396f2d",7 "completed": true,8 "ended": "2025-02-11T05:28:59.363Z",9 "isPomodoro": true,10 "minutes": 25,11 "note": "Pomodoro note containing a description of the task.",12 "started": "2025-02-11T05:02:59.382Z",13 "tags": [14 "Client ABC",15 "Project 123"16 ],17 "isManual": false,18 "tagIds": [19 "7731e24d-ba6a-43ab-b31b-86ff64f36258"20 ]21 },22 "type": "entry.updated"23}
Security features
To enhance security, Time Stream offers signing and encryption of webhook payloads.
Webhook content can be formatted in four different ways:
- JSON - The webhook content will be sent as a JSON object.
- Signed JSON - The webhook content will be signed with a secret key using JSON Web Signature (JWS).
- Encrypted JSON - The webhook content will be encrypted with a secret key using JSON Web Encryption (JWE).
- Signed and Encrypted JSON - The webhook content will first be signed using JWS, then encrypted using JWE.
JSON Web Signature (JWS)
When you choose the "Signed JSON" option, Time Stream signs the JSON payload using JWS with the HMAC-SHA256 algorithm (HS256). This provides data integrity and authenticity verification, allowing recipients to verify that the webhook came from Time Stream and that the data hasn't been tampered with during transmission.
To verify a JWS signature, you'll need the signing secret that was automatically generated when you created the webhook endpoint.
JSON Web Encryption (JWE)
When you choose the "Encrypted JSON" option, Time Stream encrypts the JSON payload using JWE with A256KW (AES Key Wrapping with 256-bit key) for key management and A256GCM (AES-GCM with 256-bit key) for content encryption. This provides confidentiality, ensuring that only the intended recipient with the encryption key can read the webhook data.
To decrypt a JWE payload, you'll need the encryption secret that was automatically generated when you created the webhook endpoint.
Combined JWS + JWE
For maximum security, you can choose the "Signed and Encrypted JSON" option, which first signs the JSON payload with JWS and then encrypts the signed payload with JWE. This provides both authenticity verification and confidentiality.
To process these webhooks, you'll need both the signing secret and the encryption secret. First decrypt the payload using the encryption secret, then verify the signature using the signing secret.
Webhook status
The webhook status is the status of the webhook. It can be one of the following:
pending
- The webhook is pending.sent
- The webhook has been sent.failed
- The webhook has failed.
The webhook status is pending when the webhook will be sent or retried at some point in the future.
The webhook status is sent when the webhook has been sent to the webhook endpoint URL and the response had a status in the 200-299 range. If the response had a status outside the 200-299 range, the webhook will be scheduled for a retry.
The webhook status is failed when the webhook has been retried up to 5 times and all attempts have failed.
CORS
Time Stream sends webhooks directly from your browser, and as such, it may trigger CORS preflighted requests.
Depending on the options you choose when creating a webhook endpoint, Time Stream may make a CORS preflighted request rather than a simple request to the webhook URL you provide.
A preflighted request enforces some requirements that need to be met by the platform or server on which the URL that will be receiving the request is hosted.
Simple requests
A simple request is a request made by the browser to the webhook endpoint URL you provide. Only one request is made and it will contain all information about the event related to an entry.
The request will be made with the following characteristics:
- A
Method: POST
request. - A
Content-Type: text/plain
header. - A JSON body containing the event data (this may or may not include a signature for verification).
Preflighted requests
A preflighted request involves making two requests to the endpoint URL you provide. The first request made by the browser is to check if the webhook endpoint URL you provide is allowed to receive webhooks. If the conditions are met, the second request is made to the webhook endpoint URL you provide.
Choosing any of the following options will trigger a preflighted request:
- Enabling the Include headers option.
- Choosing a
PUT
method.
The first request will be an OPTIONS
request with the following characteristics:
- A
Method: OPTIONS
header. - A
Content-Type: application/json
header. - An
Origin: https://my.timestream.app
header. - An
Access-Control-Request-Method: POST
header. - A
Access-Control-Request-Headers: content-type
header.
The server will need to respond with:
- An
Access-Control-Allow-Headers: content-type
header. - An
Access-Control-Allow-Methods: POST
header. - An
Access-Control-Allow-Origin: https://my.timestream.app
header. - A
204 No Content
status code.
If everything is accurate the second request will be a POST
request with the following characteristics:
- A
Method: POST
header. - A
Content-Type: application/json
header for JSON payloads, orapplication/jose
for signed or encrypted payloads. - An
Origin: https://my.timestream.app
header. - The payload in the format you selected (JSON, JWS, JWE, or JWS+JWE).
If the server responds with a status in the 200-299 range, the webhook will be considered sent.
If the server responds with a status outside the 200-299 range, the webhook will be scheduled for a retry.