@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1@title User Guide: Webhooks
2@group userguide
3
4Guide to configuring webhooks.
5
6
7Overview
8========
9
10If you'd like to react to events in Phorge or publish them into external
11systems, you can configure webhooks.
12
13Configure webhooks in {nav Herald > Webhooks}. Users must have the
14"Can Create Webhooks" permission to create new webhooks.
15
16
17Triggering Hooks
18================
19
20Webhooks can be triggered in two ways:
21
22 - Set the hook mode to **Firehose**. In this mode, your hook will be called
23 for every event.
24 - Set the hook mode to **Enabled**, then write Herald rules which use the
25 **Call webhooks** action to choose when the hook is called. This allows
26 you to choose a narrower range of events to be notified about.
27
28
29Testing Hooks
30=============
31
32To test a webhook, use {nav New Test Request} from the web interface.
33
34You can also use the command-line tool, which supports a few additional
35options:
36
37```
38phorge/ $ ./bin/webhook call --id 42 --object D123
39```
40
41
42Verifying Requests
43==================
44
45When your webhook callback URI receives a request, it didn't necessarily come
46from Phorge. An attacker or mischievous user can normally call your hook
47directly and pretend to be notifying you of an event.
48
49To verify that the request is authentic, first retrieve the webhook key from
50the web UI with {nav View HMAC Key}. This is a shared secret which will let you
51verify that Phorge originated a request.
52
53When you receive a request, compute the SHA256 HMAC value of the request body
54using the HMAC key as the key. The value should match the value in the
55`X-Phabricator-Webhook-Signature` field.
56
57To compute the SHA256 HMAC of a string in PHP, do this:
58
59```lang=php
60$signature = hash_hmac('sha256', $request_body, $hmac_key);
61```
62
63To compute the SHA256 HMAC of a string in Python, do this:
64
65```lang=python
66from subprocess import check_output
67
68signature = check_output(
69 [
70 "php",
71 "-r",
72 "echo hash_hmac('sha256', $argv[1], $argv[2]);",
73 "--",
74 request_body,
75 hmac_key
76 ])
77```
78
79Other languages often provide similar support.
80
81If you somehow disclose the key by accident, use {nav Regenerate HMAC Key} to
82throw it away and generate a new one.
83
84
85Request Format
86==============
87
88Webhook callbacks are POST requests with a JSON payload in the body. The
89payload looks like this:
90
91```lang=json
92{
93 "object": {
94 "type": "TASK",
95 "phid": "PHID-TASK-abcd..."
96 },
97 "triggers": [
98 {
99 "phid": "PHID-HRUL-abcd..."
100 }
101 ],
102 "action": {
103 "test": false,
104 "silent": false,
105 "secure": false,
106 "epoch": 12345
107 },
108 "transactions": [
109 {
110 "phid": "PHID-XACT-TASK-abcd..."
111 }
112 ]
113}
114```
115
116The **object** map describes the object which was edited.
117
118The **triggers** are a list of reasons why the hook was called. When the hook
119is triggered by Herald rules, the specific rules which triggered the call will
120be listed. For firehose rules, the rule itself will be listed as the trigger.
121For test calls, the user making the request will be listed as a trigger.
122
123The **action** map has metadata about the action:
124
125 - `test` This was a test call from the web UI or console.
126 - `silent` This is a silent edit which won't send mail or notifications in
127 Phorge. If your hook is doing something like copying events into
128 a chatroom, it may want to respect this flag.
129 - `secure` Details about this object should only be transmitted over
130 secure channels. Your hook may want to respect this flag.
131 - `epoch` The epoch timestamp when the callback was queued.
132
133The **transactions** list contains information about the actual changes which
134triggered the callback.
135
136
137Responding to Requests
138======================
139
140Although trivial hooks may not need any more information than this to act, the
141information conveyed in the hook body is a minimum set of pointers to relevant
142data and likely insufficient for more complex hooks.
143
144Complex hooks should expect to react to receiving a request by making API
145calls to Conduit to retrieve additional information about the object and
146transactions.
147
148Hooks that are interested in reading object state should generally make a call
149to a method like `maniphest.search` or `differential.revision.search` using
150the PHID from the `object` field to retrieve full details about the object
151state.
152
153Hooks that are interested in changes should generally make a call to
154`transaction.search`, passing the transaction PHIDs as a constraint to retrieve
155details about the transactions.
156
157For example, your call to `transaction.search` may look something like this:
158
159```lang=json
160{
161 "objectIdentifier": "PHID-XXXX-abcdef",
162 "constraints": {
163 "phids": [
164 "PHID-XACT-XXXX-11111111",
165 "PHID-XACT-XXXX-22222222"
166 ]
167 }
168}
169```
170
171The `phid.query` method can also be used to retrieve generic information about
172a list of objects.
173
174
175Retries and Rate Limiting
176=========================
177
178Test requests are never retried: they execute exactly once.
179
180Live requests are automatically retried. If your endpoint does not return a
181HTTP 2XX response, the request will be retried regularly until it succeeds.
182
183Retries will continue until the request succeeds or is garbage collected. By
184default, this is after 7 days.
185
186If a webhook is disabled, outstanding queued requests will be failed
187permanently. Activity which occurs while it is disabled will never be sent to
188the callback URI. (Disabling a hook does not "pause" it so that it can be
189"resumed" later and pick back up where it left off in the event stream.)
190
191If a webhook encounters a significant number of errors in a short period of
192time, the webhook will be paused for a few minutes before additional requests
193are made. The web UI shows a warning indicator when a hook is paused because of
194errors.
195
196Hook requests time out after 10 seconds. Consider offloading response handling
197to some kind of worker queue if you expect to routinely require more than 10
198seconds to respond to requests.
199
200Hook callbacks are single-threaded: you will never receive more than one
201simultaneous call to the same webhook from Phorge. If you have a firehose
202hook on an active install, it may be important to respond to requests quickly
203to avoid accumulating a backlog.
204
205Callbacks may be invoked out-of-order. You should not assume that the order
206you receive requests in is chronological order. If your hook is order-dependent,
207you can ignore the transactions in the callback and use `transaction.search` to
208retrieve a consistent list of ordered changes to the object.
209
210Callbacks may be delayed for an arbitrarily long amount of time, up to the
211garbage collection limit. You should not assume that calls are real time. If
212your hook is doing something time-sensitive, you can measure the delivery delay
213by comparing the current time to the `epoch` value in the `action` field and
214ignoring old actions or handling them in some special way.
215
216
217Next Steps
218==========
219
220Continue by:
221
222 - learning more about Herald with @{article:Herald User Guide}; or
223 - interacting with the Conduit API with @{article:Conduit API Overview}.