Skip to content

Commit fa92a21

Browse files
committed
[ADD] webhook_outgoing: send outgoing webhook requests
1 parent 848c21f commit fa92a21

23 files changed

Lines changed: 2253 additions & 0 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../../webhook_outgoing

setup/webhook_outgoing/setup.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import setuptools
2+
3+
setuptools.setup(
4+
setup_requires=['setuptools-odoo'],
5+
odoo_addon=True,
6+
)

webhook_outgoing/README.rst

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
================
2+
Outgoing Webhook
3+
================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:8bdaa6bf8f8de957410bd7bde59aa2ef3a84eaecce4da8d7d349b040e297dd77
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
18+
:alt: License: LGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fwebhook-lightgray.png?logo=github
20+
:target: https://github.com/OCA/webhook/tree/16.0/webhook_outgoing
21+
:alt: OCA/webhook
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/webhook-16-0/webhook-16-0-webhook_outgoing
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/webhook&target_branch=16.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module allows creating automations that send webhook/HTTP requests
32+
to external systems.
33+
34+
Key Features
35+
------------
36+
37+
- **Integration with Automated Actions**: Extends Odoo's automated
38+
actions with webhook capabilities
39+
- **Jinja Template Support**: Render request bodies dynamically using
40+
Jinja2 templating engine
41+
- **Queue Job Support**: Execute webhooks asynchronously using queue_job
42+
for better performance
43+
- **Multiple Request Types**: Support for standard HTTP, GraphQL, and
44+
Slack webhooks
45+
- **Flexible Configuration**: Customize endpoints, headers, body
46+
templates, and request methods
47+
- **Request Logging**: Optional logging of webhook calls for debugging
48+
and troubleshooting
49+
50+
Use Cases
51+
---------
52+
53+
- Send notifications to Slack, Discord, or other chat platforms when
54+
records are created/updated
55+
- Trigger external API calls when business events occur in Odoo
56+
- Integrate with third-party services without custom code
57+
- Synchronize data with external systems in real-time
58+
59+
**Table of contents**
60+
61+
.. contents::
62+
:local:
63+
64+
Installation
65+
============
66+
67+
Prerequisites
68+
-------------
69+
70+
This module requires the following dependencies:
71+
72+
- ``queue_job`` - For asynchronous webhook execution. Please configure
73+
``queue_job`` properly before installation.
74+
- Python ``jinja2`` library - For template rendering
75+
76+
Installation Steps
77+
------------------
78+
79+
1. Install the ``queue_job`` module from OCA/queue if you want to use
80+
asynchronous execution:
81+
82+
- Available at: https://github.com/OCA/queue
83+
84+
2. If you haven't installed ``queue_job`` before installing this module,
85+
you will need to restart the server to have ``queue_job`` loaded.
86+
87+
Post-Installation
88+
-----------------
89+
90+
After installation, the module adds a new "Custom Webhook" action type
91+
to Automated Actions. No additional configuration is required to start
92+
using the module.
93+
94+
Configuration
95+
=============
96+
97+
Navigate to **Settings > Technical > Automation > Automated Actions** to
98+
configure webhook automations.
99+
100+
Basic Configuration
101+
-------------------
102+
103+
1. **Create a New Automated Action**
104+
105+
- Click "Create"
106+
- Set the **Model** (e.g., Sale Order, Contact, etc.)
107+
- Define the **Trigger** (On Creation, On Update, etc.)
108+
- Set **Action To Do** to "Custom Webhook"
109+
110+
2. **Configure Webhook Details**
111+
112+
**Endpoint**
113+
114+
- Enter the full URL of the webhook endpoint
115+
- Example: ``https://hooks.slack.com/services/YOUR/WEBHOOK/URL``
116+
117+
**Request Method**
118+
119+
- Choose between GET or POST
120+
- Most webhooks use POST
121+
122+
**Request Type**
123+
124+
- **HTTP Request**: Standard REST API calls
125+
- **GraphQL**: For GraphQL APIs
126+
- **Slack**: Optimized for Slack webhooks
127+
128+
3. **Headers Configuration**
129+
130+
Add any required headers in JSON format:
131+
132+
.. code:: json
133+
134+
{
135+
"Content-Type": "application/json",
136+
"Authorization": "Bearer YOUR_TOKEN"
137+
}
138+
139+
4. **Body Template**
140+
141+
Use Jinja2 syntax to create dynamic payloads:
142+
143+
.. code:: jinja
144+
145+
{
146+
"id": {{ record.id }},
147+
"name": "{{ record.name }}",
148+
"email": "{{ record.email }}",
149+
"created_date": "{{ record.create_date }}"
150+
}
151+
152+
Available variables:
153+
154+
- ``record``: The record that triggered the action
155+
- Any field from the record model
156+
157+
Asynchronous Execution
158+
----------------------
159+
160+
Enable **Delay Execution** to run webhooks in the background using queue
161+
jobs:
162+
163+
- Check "Delay Execution"
164+
- Set **Delay ETA (s)** to specify how many seconds to wait before
165+
execution
166+
167+
Webhook Logging
168+
---------------
169+
170+
Enable **Log Calls** to track all webhook requests and responses for
171+
debugging:
172+
173+
- Check "Log Calls"
174+
- View logs in **Settings > Technical > Webhook Logging**
175+
176+
Domain Filters
177+
--------------
178+
179+
Use the **Apply on** field to filter which records trigger the webhook
180+
based on domain conditions.
181+
182+
Security Considerations
183+
-----------------------
184+
185+
- Use HTTPS endpoints for secure communication
186+
- Regularly review webhook logs for suspicious activity
187+
188+
Usage
189+
=====
190+
191+
Example 1: Send Slack Notification on New Sale Order
192+
----------------------------------------------------
193+
194+
1. Go to **Settings > Technical > Automation > Automated Actions**
195+
196+
2. Click **Create** and configure:
197+
198+
- **Name**: "Notify Slack on New Sale"
199+
- **Model**: Sale Order
200+
- **Trigger**: On Creation
201+
- **Action To Do**: Custom Webhook
202+
203+
3. Configure the webhook:
204+
205+
- **Endpoint**: ``https://hooks.slack.com/services/YOUR/WEBHOOK/URL``
206+
- **Request Method**: POST
207+
- **Request Type**: Slack
208+
- **Headers**:
209+
210+
.. code:: json
211+
212+
{
213+
"Content-Type": "application/json"
214+
}
215+
216+
- **Body Template**:
217+
218+
.. code:: jinja
219+
220+
{
221+
"text": "New sale order created!",
222+
"attachments": [{
223+
"color": "good",
224+
"fields": [
225+
{
226+
"title": "Order Number",
227+
"value": "{{ record.name }}",
228+
"short": true
229+
},
230+
{
231+
"title": "Customer",
232+
"value": "{{ record.partner_id.name }}",
233+
"short": true
234+
},
235+
{
236+
"title": "Amount",
237+
"value": "${{ record.amount_total }}",
238+
"short": true
239+
}
240+
]
241+
}]
242+
}
243+
244+
Example 2: POST to External API
245+
-------------------------------
246+
247+
Configure a webhook to send contact data to an external CRM:
248+
249+
- **Endpoint**: ``https://api.example.com/contacts``
250+
- **Request Method**: POST
251+
- **Request Type**: HTTP Request
252+
- **Headers**:
253+
254+
.. code:: json
255+
256+
{
257+
"Content-Type": "application/json",
258+
"Authorization": "Bearer YOUR_API_TOKEN"
259+
}
260+
261+
- **Body Template**:
262+
263+
.. code:: jinja
264+
265+
{
266+
"external_id": {{ record.id }},
267+
"first_name": "{{ record.name }}",
268+
"email": "{{ record.email }}",
269+
"phone": "{{ record.phone }}",
270+
"company": "{{ record.company_id.name if record.company_id else '' }}"
271+
}
272+
273+
Example 3: GraphQL Query
274+
------------------------
275+
276+
Send a GraphQL mutation when a product is updated:
277+
278+
- **Endpoint**: ``https://api.example.com/graphql``
279+
- **Request Method**: POST
280+
- **Request Type**: GraphQL
281+
- **Body Template**:
282+
283+
.. code:: jinja
284+
285+
mutation {
286+
updateProduct(input: {
287+
id: {{ record.id }}
288+
name: {{ record.name | escape }}
289+
price: {{ record.list_price }}
290+
}) {
291+
id
292+
statusCode
293+
}
294+
}
295+
296+
Using Jinja2 Templates
297+
----------------------
298+
299+
**Available Variables**
300+
301+
- ``record``: The current record triggering the action
302+
- Access related fields using dot notation: ``record.partner_id.name``
303+
304+
**Common Jinja2 Filters**
305+
306+
.. code:: jinja
307+
308+
{# String manipulation #}
309+
{{ record.name | upper }}
310+
{{ record.description | truncate(100) }}
311+
312+
{# Default values #}
313+
{{ record.email | default('no-email@example.com') }}
314+
315+
{# Conditional rendering #}
316+
{% if record.state == 'sale' %}
317+
"status": "confirmed"
318+
{% else %}
319+
"status": "draft"
320+
{% endif %}
321+
322+
{# Escaping for GraphQL #}
323+
{{ record.name | escape }}
324+
325+
Bug Tracker
326+
===========
327+
328+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/webhook/issues>`_.
329+
In case of trouble, please check there if your issue has already been reported.
330+
If you spotted it first, help us to smash it by providing a detailed and welcomed
331+
`feedback <https://github.com/OCA/webhook/issues/new?body=module:%20webhook_outgoing%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
332+
333+
Do not contact contributors directly about support or help with technical issues.
334+
335+
Credits
336+
=======
337+
338+
Authors
339+
-------
340+
341+
* Hoang Tran
342+
343+
Contributors
344+
------------
345+
346+
- Hoang Tran thhoang.tr@gmail.com
347+
348+
Maintainers
349+
-----------
350+
351+
This module is maintained by the OCA.
352+
353+
.. image:: https://odoo-community.org/logo.png
354+
:alt: Odoo Community Association
355+
:target: https://odoo-community.org
356+
357+
OCA, or the Odoo Community Association, is a nonprofit organization whose
358+
mission is to support the collaborative development of Odoo features and
359+
promote its widespread use.
360+
361+
This module is part of the `OCA/webhook <https://github.com/OCA/webhook/tree/16.0/webhook_outgoing>`_ project on GitHub.
362+
363+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

webhook_outgoing/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models

webhook_outgoing/__manifest__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2024 Hoang Tran <thhoang.tr@gmail.com>.
2+
# License LGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
3+
4+
{
5+
"name": "Outgoing Webhook",
6+
"summary": "Webhook to publish events based on automated triggers",
7+
"version": "16.0.0.0.1",
8+
"author": "Hoang Tran,Odoo Community Association (OCA)",
9+
"license": "LGPL-3",
10+
"website": "https://github.com/OCA/webhook",
11+
"depends": [
12+
"base_automation",
13+
"queue_job",
14+
],
15+
"data": [
16+
"security/ir.model.access.csv",
17+
"data/queue_data.xml",
18+
"views/webhook_logging_views.xml",
19+
"views/ir_action_server_views.xml",
20+
"views/menus.xml",
21+
],
22+
"installable": True,
23+
}

0 commit comments

Comments
 (0)