From 5f2cf8d3ef0d0a02a6308789c09f6f27e33a29e7 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Thu, 8 Nov 2018 16:53:25 +0100 Subject: [PATCH] Add documentation on webhooks --- doc/api/resources/webhooks.rst | 243 +++++++++++++++++++++++++++++++++ doc/api/webhooks.rst | 105 ++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 doc/api/resources/webhooks.rst create mode 100644 doc/api/webhooks.rst diff --git a/doc/api/resources/webhooks.rst b/doc/api/resources/webhooks.rst new file mode 100644 index 0000000000..64174f6d69 --- /dev/null +++ b/doc/api/resources/webhooks.rst @@ -0,0 +1,243 @@ +.. _`rest-webhooks`: + +Webhooks +======== + +.. note:: This page is about how to modify webhook settings themselves through the REST API. If you just want to know + how webhooks work, go here: :ref:`webhooks` + +Resource description +-------------------- + +The webhook resource contains the following public fields: + +.. rst-class:: rest-resource-table + +===================================== ========================== ======================================================= +Field Type Description +===================================== ========================== ======================================================= +id integer Internal ID of the webhook +enabled boolean If ``False``, this webhook will not receive any notifications +target_url string The URL to call +all_events boolean If ``True``, this webhook will receive notifications + on all events of this organizer +limit_events list of strings If ``all_events`` is ``False``, this is a list of + event slugs this webhook is active for +action_types list of strings A list of action type filters that limit the + notifications sent to this webhook. See below for + valid values +===================================== ========================== ======================================================= + +The following values for ``action_types`` are valid with pretix core: + + * ``pretix.event.order.placed`` + * ``pretix.event.order.paid`` + * ``pretix.event.order.canceled`` + * ``pretix.event.order.expired`` + * ``pretix.event.order.modified`` + * ``pretix.event.order.contact.changed`` + * ``pretix.event.order.changed.*`` + * ``pretix.event.order.refund.created.externally`` + * ``pretix.event.order.refunded`` + * ``pretix.event.order.approved`` + * ``pretix.event.order.denied`` + * ``pretix.event.checkin`` + * ``pretix.event.checkin.reverted`` + +Installed plugins might register more valid values. + + +Endpoints +--------- + +.. http:get:: /api/v1/organizers/(organizer)/webhooks/ + + Returns a list of all webhooks within a given organizer. + + **Example request**: + + .. sourcecode:: http + + GET /api/v1/organizers/bigevents/webhooks/ HTTP/1.1 + Host: pretix.eu + Accept: application/json, text/javascript + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "id": 2, + "enabled": true, + "target_url": "https://httpstat.us/200", + "all_events": false, + "limit_events": ["democon"], + "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"] + } + ] + } + + :query integer page: The page number in case of a multi-page result set, default is 1 + :param organizer: The ``slug`` field of the organizer to fetch + :statuscode 200: no error + :statuscode 401: Authentication failure + :statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource. + +.. http:get:: /api/v1/organizers/(organizer)/webhooks/(id)/ + + Returns information on one webhook, identified by its ID. + + **Example request**: + + .. sourcecode:: http + + GET /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1 + Host: pretix.eu + Accept: application/json, text/javascript + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + { + "id": 2, + "enabled": true, + "target_url": "https://httpstat.us/200", + "all_events": false, + "limit_events": ["democon"], + "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"] + } + + :param organizer: The ``slug`` field of the organizer to fetch + :param id: The ``id`` field of the webhook to fetch + :statuscode 200: no error + :statuscode 401: Authentication failure + :statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource. + +.. http:post:: /api/v1/organizers/(organizer)/webhooks/ + + Creates a new webhook + + **Example request**: + + .. sourcecode:: http + + POST /api/v1/organizers/bigevents/webhooks/ HTTP/1.1 + Host: pretix.eu + Accept: application/json, text/javascript + Content: application/json + + { + "enabled": true, + "target_url": "https://httpstat.us/200", + "all_events": false, + "limit_events": ["democon"], + "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"] + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 201 Created + Vary: Accept + Content-Type: application/json + + { + "id": 3, + "enabled": true, + "target_url": "https://httpstat.us/200", + "all_events": false, + "limit_events": ["democon"], + "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"] + } + + :param organizer: The ``slug`` field of the organizer to create a webhook for + :statuscode 201: no error + :statuscode 400: The webhook could not be created due to invalid submitted data. + :statuscode 401: Authentication failure + :statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource. + +.. http:patch:: /api/v1/organizers/(organizer)/webhooks/(id)/ + + Update a webhook. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of + the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you + want to change. + + You can change all fields of the resource except the ``id`` field. + + **Example request**: + + .. sourcecode:: http + + PATCH /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1 + Host: pretix.eu + Accept: application/json, text/javascript + Content-Type: application/json + Content-Length: 94 + + { + "enabled": false + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Vary: Accept + Content-Type: application/json + + { + "id": 1, + "enabled": false, + "target_url": "https://httpstat.us/200", + "all_events": false, + "limit_events": ["democon"], + "action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"] + } + + :param organizer: The ``slug`` field of the organizer to modify + :param id: The ``id`` field of the webhook to modify + :statuscode 200: no error + :statuscode 400: The webhook could not be modified due to invalid submitted data + :statuscode 401: Authentication failure + :statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource. + +.. http:delete:: /api/v1/organizers/(organizer)/webhook/(id)/ + + Delete a webhook. Currently, this will not delete but just disable the webhook. + + **Example request**: + + .. sourcecode:: http + + DELETE /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1 + Host: pretix.eu + Accept: application/json, text/javascript + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 No Content + Vary: Accept + + :param organizer: The ``slug`` field of the organizer to modify + :param id: The ``id`` field of the webhook to delete + :statuscode 204: no error + :statuscode 401: Authentication failure + :statuscode 403: The requested organizer does not exist **or** you have no permission to delete this resource. diff --git a/doc/api/webhooks.rst b/doc/api/webhooks.rst new file mode 100644 index 0000000000..ce6a3ade8c --- /dev/null +++ b/doc/api/webhooks.rst @@ -0,0 +1,105 @@ +.. _`webhooks`: + +Webhooks +======== + +pretix can send webhook calls to notify your application of any changes that happen inside pretix. This is especially +useful for everything triggered by an actual user, such as a new ticket sale or the arrival of a payment. + +You can register any number of webhook URLs that pretix will notify any time one of the supported events occurs inside +your organizer account. A great example use case of webhooks would be to add the buyer to your mailing list every time +a new order comes in. + +Configuring webhooks +-------------------- + +You can find the list of your active webhooks in the "Webhook" section of your organizer account: + +.. thumbnail:: ../screens/organizer/webhook_list.png + :align: center + :class: screenshot + +Click "Create webhook" if you want to add a new URL. You will then be able to enter the URL pretix shall call for +notifications. You need to select any number of notification types that you want to receive and you can optionally +filter the events you want to receive notifications for. + +.. thumbnail:: ../screens/organizer/webhook_edit.png + :align: center + :class: screenshot + +You can also configure webhooks :ref:`through the API itself `. + +Receiving webhooks +------------------ + +Creating a webhook endpoint on your server is no different from creating any other page on your website. If your +website is written in PHP, you might just create a new ``.php`` file on your server; if you use a web framework like +Symfony or Django, you would just create a new route with the desired URL. + +We will call your URL with a HTTP ``POST`` request with a ``JSON`` body. In PHP, you can parse this like this:: + + $input = @file_get_contents('php://input'); + $event_json = json_decode($input); + // Do something with $event_json + +In Django, you would create a view like this:: + + def my_webhook_view(request): + event_json = json.loads(request.body) + # Do something with event_json + return HttpResponse(status=200) + +More samples for the language of your choice are easy to find online. + +The exact body of the request varies by notification type, but for the main types included with pretix core, such as +those related to changes of an order, it will look like this:: + + { + "notification_id": 123455, + "organizer": "acmecorp", + "event": "democon", + "code": "ABC23", + "action": "pretix.event.order.placed" + } + +Notifications regarding a check-in will contain more details like ``orderposition_id`` +and ``checkin_list``. + +.. warning:: You should not trust data supplied to your webhook, but only use it as a trigger to fetch updated data. + Anyone could send data there if they guess the correct URL and you won't be able to tell. Therefore, we + only include the minimum amount of data necessary for you to fetch the changed objects from our + :ref:`rest-api` in an authenticated way. + +If you want to further prevent others from accessing your webhook URL, you can also use `Basic authentication`_ and +supply the URL to us in the format of ``https://username:password@domain.com/path/``. +We recommend that you use HTTPS for your webhook URL and might require it in the future. If HTTPS is used, we require +that a valid certificate is in use. + +.. note:: If you use a web framework that makes use of automatic CSRF protection, this protection might prevent us + from calling your webhook URL. In this case, we recommend that you turn of CSRF protection selectively + for that route. In Django, you can do this by putting the ``@csrf_exempt`` decorator on your view. In + Rails, you can pass an ``except`` parameter to ``protect_from_forgery``. + + +Responding to a webhook +----------------------- + +If you successfully received a webhook call, your endpoint should return a HTTP status code between ``200`` and ``299``. +If any other status code is returned, we will assume you did not receive the call. This does mean that any redirection +or ``304 Not Modified`` response will be treated as a failure. pretix will not follow any ``301`` or ``302`` redirect +headers and pretix will ignore all other information in your response headers or body. + +If we do not receive a status code in the range of ``200`` and ``299``, pretix will retry to deliver for up to three +days with an exponential back off. Therefore, we recommend that you implement your endpoint in a way where calling it +multiple times for the same event due to a perceived error does not do any harm. + +There is only one exception: If status code ``410 Gone`` is returned, we will assume the +endpoint does not exist any more and automatically disable the webhook. + +Debugging webhooks +------------------ + +If you want to debug your webhooks, you can view a log of all sent notifications and the responses of your server for +30 days right next to your configuration. + +.. _Basic authentication: https://en.wikipedia.org/wiki/Basic_access_authentication