Custom events
GitHub and Jira events reach gritz through their own integrations. Any other
system can send events too: you define a custom event type, and the system
POSTs events of that type to the custom events webhook with an API key.
Routing rules then decide what each event does, as they do for built-in events:
create a task, wake a task that’s already working on the same resource, or both.
This page uses an alerting system as its example. It posts an alert event when
an alert fires, and gritz creates a task to investigate it.
1. Define a custom event type
Section titled “1. Define a custom event type”Open Events → Custom Schemas and add a type with + Type:
- Type: the name senders put in the envelope’s
typefield, for examplealert. It must start with a lowercase letter, followed by up to 63 lowercase letters, digits and underscores (^[a-z][a-z0-9_]{0,63}$). It must be unique among your organisation’s custom types, and it can’t be renamed later: remove the type and add a new one instead. - Label: shown in the routing rule editor and on the task’s timeline, for
example
Alert. It can’t be empty. - Attributes: the values routing rules can match on. Every type has
bodyandurl; the form adds both and doesn’t let you remove them. Add one row per extra attribute, with a key that follows the same rules as the type name. The example declaresseverityandservice. An attribute’s label, help and placeholder are only for display, in the rule editor and the JSON Schema.
The JSON Schema button next to each type shows the request body the webhook accepts for it.
2. Create an API key
Section titled “2. Create an API key”Senders authenticate with an API key. Create one on the Keys page
(https://gritz.dev/ui/keys/new) and copy the token: it starts with xat_ and
is shown only once. The key belongs to the organisation you create it in, and so
does every event sent with it.
Store the key as a secret in the sending system and send it as a bearer token:
Authorization: Bearer xat_…The webhook URL is on Settings → Organisation, in the Custom Events
card. For gritz.dev it is https://gritz.dev/webhook/custom.
3. Send an event
Section titled “3. Send an event”POST a JSON envelope to the webhook URL:
curl https://gritz.dev/webhook/custom \ -H "Authorization: Bearer $GRITZ_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "alert", "url": "https://alerts.example.com/alerts/4211", "body": "checkout-api error rate above 5% for 10 minutes", "attrs": {"severity": ["critical"], "service": ["checkout-api"]}, "details": {"runbook": "https://wiki.example.com/runbooks/checkout-api"} }'The command reads the key from a variable, so it can be shared or logged without the key’s value.
typenames the custom type.urlidentifies the resource the event is about; see The URL is the subscription key.bodyis the event’s text. The agent reads it, and rules can match on it.attrsholds the values of the type’s other attributes. Each value is a list of strings, even when there is only one.detailsholds extra key/value context that is shown with the event but that rules can’t match on.
Unknown fields are rejected. The envelope reference lists every field and its validation.
A valid event gets a 200 response with the number of tasks it created or
woke:
{"routed": 1}{"routed": 0} is still a success: the event was valid, but nothing acted on it
(see Troubleshooting).
4. Add a routing rule
Section titled “4. Add a routing rule”An event does nothing until a routing rule matches it. Open Events → Routing Rules and add a rule with + Rule:
- Event type: the type’s label, here
Alert. - Conditions:
severityequalscritical. A condition matches when any of the attribute’s values satisfies it, and the rule matches when every condition does. - Public: on. Custom event rules must be public; see below.
- Wake up linked tasks: on.
- Create a task: on, with a runner, a workspace, and a prompt such as “Investigate this alert and report the likely cause.”
A task created by a rule is subscribed to the event’s url. The next event with
the same url wakes that task instead of creating another one.
Why custom event rules must be public
Section titled “Why custom event rules must be public”A rule that isn’t public only fires for events whose actor is a member of the organisation, such as a GitHub comment by a member. A custom event has no actor: it belongs to the API key’s organisation, and only that organisation’s public rules are considered for it. A member-only rule for a custom type could never fire, so gritz rejects it when you save it.
Follow-up events
Section titled “Follow-up events”For each event, rules are checked in the order shown on the Routing Rules tab
(the arrows reorder them), and the first rule that matches wins. The rule above
only matches critical alerts, so when the alerting system later posts a
resolved event for the same alert, it matches nothing and never reaches the
task.
Add a second rule below the first: the same type, no conditions, public, Wake
up linked tasks on and Create a task off. The resolved event matches it and
wakes the task subscribed to its url. An event for an alert gritz never saw
fire has no subscribed task, so this rule does nothing for it rather than create
a task.
The URL is the subscription key
Section titled “The URL is the subscription key”gritz decides which task an event belongs to by its url: an event wakes the
tasks subscribed to that URL. A task is subscribed to the url of the event that
created it, and to any link an agent creates with subscribe=true.
For most URLs, gritz uses the URL exactly as sent, so every event about the same resource must send the same URL, byte for byte. A different query string, a trailing slash or a different host name is a different resource, and the event won’t wake the task the first event created.
GitHub and Jira URLs are the exception: gritz normalises them as it does for their native events, so a custom event about a GitHub pull request or a Jira issue wakes the task subscribed to it, whichever form of the URL each side used. The envelope reference lists the forms.
Pick a URL that a person can open, such as the alert’s page in the alerting system, and send exactly that string in every event about the alert.
Troubleshooting
Section titled “Troubleshooting”| Status | Response body | Cause |
|---|---|---|
400 |
invalid envelope: … |
The body isn’t valid JSON, has a field the envelope doesn’t define, has a field of the wrong type (for example an attr value that is a string rather than a list), or is larger than 1 MiB. |
400 |
unknown event type "…" |
type is empty or doesn’t name one of the organisation’s custom types. Check Events → Custom Schemas. |
400 |
url is required |
url is missing or empty. |
400 |
url "…" must be an absolute http or https URL |
url is relative, has no host, or uses another scheme. |
400 |
attr "body" must be sent as the envelope's "body" field |
body or url was sent inside attrs. Send it as the top-level field. |
400 |
attr "…" not declared by event type "…" |
attrs has a key the type doesn’t declare. Add the attribute to the type, or drop it from the envelope. |
401 |
bearer token required |
No Authorization: Bearer … header. A browser session cookie isn’t accepted. |
401 |
invalid or expired token |
The key is wrong, deleted or expired. |
403 |
permission denied |
The token can’t create events, for example a task’s own token. Use an API key. |
200 |
{"routed": 0} |
No public rule for the type matched the event, or a rule matched but no task is subscribed to the url and the rule doesn’t create tasks. |