Skip to content

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.

Open Events → Custom Schemas and add a type with + Type:

  • Type: the name senders put in the envelope’s type field, for example alert. 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 body and url; 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 declares severity and service. 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.

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.

POST a JSON envelope to the webhook URL:

Terminal window
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.

  • type names the custom type.
  • url identifies the resource the event is about; see The URL is the subscription key.
  • body is the event’s text. The agent reads it, and rules can match on it.
  • attrs holds the values of the type’s other attributes. Each value is a list of strings, even when there is only one.
  • details holds 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).

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: severity equals critical. 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.

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.

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.

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.

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.