Event sources
Supported event source types and their event types
Event source instructions
You can provide custom instructions for each event source. These instructions will be shown to the user when installing the snap-in. The instructions are written in markdown and can be provided in the setup_instructions field of the event source.
To provide dynamic instructions, you can access some metadata from event source objects. The following fields are accessible:
- {{source.name}}: Name of the event source
- {{source.trigger_url}}: Webhook URL
- {{source.config.<field_name>}}: Fields from within the event source configuration
For example:
1 event-sources:2 - name: argocd-source3 description: Events triggered by ArgoCD workflows4 display_name: ArgoCD5 type: flow-custom-webhook6 setup_instructions: |7 ## ArgoCD webhook8 You webhook URL is `{{source.trigger_url}}`. You can use it to configure ArgoCD webhook.9 The documentation is available over [here](https://argo-cd.readthedocs.io/en/stable/operator-manual/notifications/services/webhook/).10 config:11 policy: |12 package rego13 output = {"event": event, "event_key": event_key} {14 event := input.request.body15 event_key := "argocd.workflow"16 } else = {"response": response } {17 response := {18 "status_code": 40019 }20 }
DevRev Webhook
The devrev-webhook event source can be used to listen for events occuring on DevRev. These includes events like work_created and conversation_updated. The payload of the events is defined by the Webhooks Event Schema.
Webhook Filters
To limit the events being sent to a webhook, you can also specify a jq query as a filter. This filter is applied on the webhook before the event is dispatched. If no filter is specified, then all events are sent.
The jq query can reference the event payload as . and the user creating the webhook as $user. The $user object only contains one string field: the id of the webhook creator.
The filter must return a boolean value. If the filter returns true, the event is dispatched to the webhook. If the filter returns false, the event is not dispatched to the webhook.
For example, to create an event source which listens for issue creation and comments on issues, you can define the event source as follows:
1 event-sources:2 - name: devrev-webhook3 description: Events coming from DevRev for issues4 display_name: DevRev webhook5 type: devrev-webhook6 config:7 event_types:8 - work_created9 - timeline_entry_created10 filter:11 jq_query: |12 if .type == "work_created" then13 if (.work_created.work.type == "issue") then true14 else false15 end16 elif .type == "timeline_entry_created" then17 if (.timeline_entry_created.entry.type == "timeline_comment" and .timeline_entry_created.entry.object_type == "issue") then true18 else false19 end20 else false21 end
Scheduled events
flow-events are an event source that snap-ins can use to publish or schedule events directly. This event source can then be used to trigger another automation. This capability makes it ideal for building snap-ins that need to act later. For example, sending reminders to issues still in the Triage stage.
You create an event source of type flow-events, and from your snap-in, you call the DevRev API to schedule an event for this event source. The following example illustrates how this works:
manifest.yaml
1 version: "1"2 3 service_account:4 display_name: Test bot5 6 event-sources:7 - name: devrev-webhook8 description: Event coming from DevRev9 display_name: DevRev10 type: devrev-webhook11 config:12 event_types:13 - work_created14 - name: scheduled-events15 description: Events scheduled from Snap-ins16 display_name: scheduled-events17 type: flow-events18 19 functions:20 - name: function_121 description: Function 122 - name: function_223 description: Function 224 25 automations:26 - name: Schedule an event 30 seconds after work is created27 source: devrev-webhook28 event_types:29 - work_created30 function: function_131 - name: Comment hello on scheduled work item32 source: scheduled-events33 event_types:34 - custom:work-created-scheduled-event35 function: function_2
In this example, the first automation schedules an event with a delay of 30 seconds for the event source scheduled-events.
The second automation then binds function_2 to this event source. So the scheduled event will execute function_2 30 seconds after the work is created.
In function_1, we can publish the event as follows:
1 const url = "https://api.devrev.ai/event-sources.schedule";2 const work = event.payload.work_created.work;3 const delay_secs = 30;4 const event_payload = {5 "object_id": work.id,6 "name" : work.created_by.full_name,7 "delay": delay_secs,8 }9 const payload_bytes = Buffer.from(JSON.stringify(event_payload)).toString('base64');10 const publish_at = new Date(Date.now() + 1000 * delay_secs).toISOString();11 12 const req = {13 "id": event.input_data.event_sources["scheduled-events"],14 "payload": payload_bytes,15 "event_type": "work-created-scheduled-event",16 "publish_at": publish_at,17 "event_key": `delayed-run-${work.id}`18 }19 const response = await axios.post(url, req, {20 headers: {21 'Content-Type': 'application/json',22 authorization: event.context.secrets.service_account_token23 }24 });25 console.log('response: ', response.data);
In function_2, we can use the payload of the scheduled event as follows:
1 const object_id = event.payload.object_id;2 const name = event.payload.name;3 const delay = event.payload.delay;4 console.log(`Received payload with object_id: ${object_id}, name: ${name}, delay: ${delay}`);
The /event-sources.schedule API is currently in early access. For API details, see event-sources-schedule-event.
To cancel scheduled events, you can utilize the /event-sources.unschedule API. For API details, see event-sources-delete-scheduled-event.
Generic event sources
You can create a generic event source if you need an event source not supported by DevRev. A generic event source includes a webhook URL and developer-provided code on how to validate the events coming on the URL.
For example, let's say you want to connect to a source that can publish events to a webhook URL, and it hashes the entire request payload with the shared secret and adds an HTTP header X-Signature-256 with value as the hash. Then, you can provide the custom code (currently in rego) to validate the payload with the same algorithm. You would also calculate the hash and verify with the value present in the header to avoid someone else tampering in between.
To create a generic event source in your manifest, you create an event source with the type flow-custom-webhook and config containing a key policy which contains the rego code.
The input passed to the policy is the following JSON:
1 {2 "parameters": "<a JSON which contains the parameters you have specified in your event source's `config.parameters` field>",3 "request": {4 "method": "<HTTP method of the request (GET, POST, etc.)>",5 "query": "<key-value map of the query parameters in the request URL. The keys are case sensitive. This is of type map[string][]string.6 e.g. if the query string is `a=1&b=2&b=3&c[]=4&c[]=5`, then the value of this field is `{"a": ["1"], "b": ["2", "3"], "c[]": ["4", "5"]}`>",7 "query_raw": "<The raw query string without the '?'.>",8 "headers": "<key-value (string) map of the request headers. Duplicate values for same `key` are overridden.9 Header keys are in Canonical-Header-Key format. E.g. Content-Type, Accept-Encoding, etc.>",10 "body": "<JSON body of the HTTP request received on the webhook. If Content-Type is application/x-www-form-urlencoded, then the body is parsed in the same way as the `query` field.",11 "body_raw": "<Raw body (bytes) of the HTTP request as a base64 string.>"12 }13 }
The policy must return an object output in the following format:
1 {2 "event": "[optional]3 A JSON representing the event to emit.4 This is `input.request.body` in most cases.5 If it is not present (or null), then the event is not published.",6 "event_key": "[required and non-empty if `event` is non-null]7 `string` to represent the type of the event.8 E.g. file-created, pipeline-failed etc.9 This `event_key` is prefixed with 'custom:' and published to DevRev.10 So, Snap-ins that want to use this event should use the `event_type` as `custom:event-key` in their automations.",11 "response": `[optional]12 A JSON containing following fields:13 {14 "status_code": "[optional]15 `integer` HTTP status code to return to the caller.16 Only valid HTTP codes allowed.17 Defaults to 200.",18 "body": "[optional]19 `string` or `JSON` for HTTP body to return to the client.",20 "headers": "[optional]21 `JSON` with each key and value of type string to be returned in the HTTP response."22 }`23 }
Example of a generic event source. This event source authenticates the incoming request by checking the header X-Signature-256.
1 event-sources:2 - name: custom-webhook3 description: Events sent on a custom webhook connected to an external system4 display_name: Custom External Webhook5 type: flow-custom-webhook6 setup_instructions: |7 ## Custom webhook8 - You webhook URL is `{{source.trigger_url}}`.9 - Your secret is `{{source.config.parameters.secret}}`.10 - The webhook will be triggered when you send a POST request to the URL.11 - The request must have a header `X-Signature-256` with value as `HMAC-SHA256` of the request body using the secret as the key.12 config:13 policy: |14 package rego15 payload_as_string := base64.decode(input.request.body_raw) ## body_raw is a base64 encoded string16 expected_signature := crypto.hmac.sha256(payload_as_string, input.parameters.secret)17 output = {"event": event, "event_key": event_key} {18 input.request.method == "POST"19 expected_signature == input.request.headers["X-Signature-256"]20 event := input.request.body21 event_key := "my-event"22 } else = {"response": response} {23 response := {24 "status_code": 401,25 "body": "Unauthenticated"26 }27 }28 parameters:29 secret: "EnterYourRandomSecretHere"
The event_key output of the policy can be used to define an automation on the event custom:event_key. For example:
1 automations:2 - name: Run on custom webhook3 source: custom-webhook4 event_types:5 - "custom:my-event"6 function: function_1
Here, we are creating an event source with the name custom-webhook, and the policy validates that there should be a header X-Signature-256 with a value of HMAC-SHA256 of raw request body.
A sample curl command to trigger this event source manually is:
1 curl -i -H 'X-Signature-256: f1809d5135917b311644058cf1994c5ff4898ad20dbf6e282c1433e6be4e2fe1' \2 -d '{"hello":"world"}' \3 https://api.devrev.ai/hidden/dev-orgs/DEV-123/event-source-webhooks/custom/d43fc297-03d7-4cbd-bdf9-044847788306
Timer-based event sources
Timer-based event sources can be created to send events based on intervals and cron schedules. In the following example, we have two event sources, one emits events daily at 12:00am, the other every hour (3600 seconds). In the event payload, you will see the JSON field metadata you specified in the event source configuration.
1 - name: daily-timer-source2 description: Timer event source based on Cron expression3 display_name: Timer source4 type: timer-events5 config:6 cron: "0 0 * * *"7 metadata:8 event_key: daily_events9 10 - name: hourly-events11 description: Timer event source based on interval seconds12 display_name: Timer source13 type: timer-events14 config:15 interval_seconds: 360016 metadata:17 event_key: hourly_events
The automation event type for timer events is timer.tick. To initiate an automation based on timer events, use the following syntax in manifest.yaml:
1 automations:2 - name: <name-of-automation>3 source: <timer-event-name>4 event_types:5 - timer.tick6 function: <func-name>
The payload contains the metadata fields you specified in the event source configuration. For example, {"event_key": "daily_events"} for the daily-timer-source.
Email-based event sources
You can create an email-based event source that can be used to write automations on top of emails. For this, a sample of the YAML is shown below:
1 - name: email-event-source2 description: Event source which emits an event when an email is received.3 display_name: Email events listener4 type: email-forward5 config:6 allowlist:7 - "test@company.com"8 - "mycompanydomain"
The event type of the events emitted by this event source is email.receive.
When a snap-in using such an event source is deployed, the instructions will show a unique email address. You will have to set up forwarding in your original email inbox or Google Group to this email address. In the example above, if the forwarding address is v1.abc.xyz@hooks.devrev.ai, we should set up email forwarding from test@company.com to v1.abc.xyz@hooks.devrev.ai.
Only emails forwarded from the allowlist of emails/domains will be emitted as events. Hence, this event source should be used for email forwarding only, not as a direct mailbox to which anyone can send their emails. Emails received (either directly or by forwarding) from senders who aren't in the allowlist will be dropped. There are a few exceptions to this like Google's forwarding confirmation emails.
The allowlist can either contain an email address or an entire domain. Strict matching is done for domains, meaning that subdomains are not included.
The email protocol regarding forwarding is not very well defined. We've tested forwarding from common sources like Gmail, Google Groups, and Outlook Inbox. However, if you're having issues with email forwarding on other providers, feel free to contact us.
Payload
Below is a sample payload of the events produced by the email event source:
1 {2 "emailBody": "test reply\r\n\r\nOn Thu, Sep 7, 2023 at 3:23 PM Test User <test@example.com> wrote:\r\n\r\n> test email body\r\n>\r\n",3 "from": [4 "test@example.com"5 ],6 "to": [7 "test@company.com"8 ],9 "cc": [],10 "bcc": [],11 "htmlBody": "<div dir=\"ltr\"><div>test reply</div><div><br></div><div class=\"gmail_quote\"><div dir=\"ltr\" class=\"gmail_attr\">On Thu, Sep 7, 2023 at 3:23 PM Test User <<a href=\"mailto:test@example.com\">test@example.com</a>> wrote:<br></div><blockquote class=\"gmail_quote\" style=\"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex\"><div dir=\"ltr\">test email body<br></div>\r\n</blockquote></div></div>\r\n",12 "inReplyToId": {13 "stringValues": [14 "jksdnfjnsflkdsfkjaabcdefghiuK8BA@mail.gmail.com"15 ]16 },17 "messageId": "ABCdEfghijklmnopqrstuvwZHL+AB_XYZg@mail.gmail.com",18 "rawBodyArtifactId": "don:core:dvrv-us-1:devo/802:artifact/296",19 "referenceIds": {20 "stringValues": [21 "abcdabc123123123@mail.gmail.com"22 ]23 },24 "artifactIds": [25 "don:core:dvrv-us-1:devo/802:artifact/1"26 ],27 "replyTo": {28 "stringValues": [29 "test+123@example.com"30 ]31 },32 "sentOn": "2023-09-07T09:56:37Z",33 "subject": "Re: test email title"34 }
Below is a brief description of the fields:
- emailBody: The plain text body of the email.
- htmlBody: The HTML body of the email.
- from: List of from email addresses of the email.
- to, cc, bcc: List of recipient email addresses.
- inReplyToId: Message ID of the email (if received mail is a reply to some other previous mail).
- messageId: Message ID of the received email.
- rawBodyArtifactId: Artifact ID for the raw email (in MIME format) received on the event source. You can use the artifact APIs to download this email if you need to.
- artifactIds: Artifact IDs for attachments in the email.
- replyTo: Reply-To header of the email.
- sentOn: Timestamp when email was sent (in UTC).
- subject: Subject of the email.