openapi: 3.0.2 info: title: Echo Webhooks API version: 1.0.4 contact: name: Eveoh url: 'https://developer.eveoh.nl' email: support@eveoh.nl description: | This API specification describes the webhook functionality of the Echo API. Webhooks allow apps to receive HTTP callbacks in real-time when events happen in Echo. The [Webhook call](#operation/WebhookCall) POST endpoint describes the requests a receiving endpoint can expect. ### Creating an endpoint for webhooks The receiving endpoint must: - be accessible via an HTTPS address with a valid SSL certificate - be able to correctly process `event` requests - never send a response larger that `32KB`. ### Receiving data Echo will make a HTTP `POST` request to the configured webhook URL. All requests will be sent with content type `application/json` and will contain an `event` message with a `data` property that is specific to the type of event. Please refer to the [Webhook call](#operation/WebhookCall) POST endpoint for details on the exact contents of the request body and headers. *At least once* delivery is used. This means that events might be delivered multiple times, for example as a result of [error handling](#error-handling). Endpoints can use the `X-Echo-Delivery-Id` HTTP header to deduplicate events. Echo supports Basic Authentication and Bearer tokens via the `Authorization` HTTP header. It is up to the receiving endpoint to validate the credentials. ### Responding to a webhook Receiving endpoints must return a HTTP status code `2xx` to acknowledge it received the request. You should return an empty body with your response. Any other HTTP status codes indicates that you did not receive the webhook and will be considered an error response. This includes HTTP `3xx` redirection codes, these will not be followed by Echo. Endpoints must never send a response larger that `32KB`. Receiving endpoints must respond to Echo within 10 seconds. If no response is received within 10 seconds, this will be considered an error response. It is best practice to acknowledge webhook requests immediately, and defer processing of the request (i.e. making additional network calls, performing complex logic, et cetera). ### Error handling If a request to the receiving endpoints fails, Echo will start a retry process. As a result of this, events might be delivered multiple times (for example when an endpoint _did_ receive a message, but _did not_ respond within 10 seconds). Endpoints can use the `X-Echo-Delivery-Id` HTTP header to deduplicate events. Echo might drop events if a certain delivery queue size threshold is exceeded due to retries. The oldest events will be dropped first. When endpoints are down for an extended period of time, they might be automatically disabled. Endpoints are responsible for discarding events that are not relevant anymore. For example, events that are bound to a certain time (e.g. a scheduled activity from a timetable) that has already passed should not be forwarded to end-users. # Authentication Receiving endpoints can implement two types of authentication: - Basic HTTP Authentication - Authentication using Bearer tokens servers: - url: 'https://{baseUrl}' description: The URL of your server listening for Webhook calls. variables: baseUrl: default: example.org paths: /: description: '' post: tags: - Calls summary: Webhook call description: |- Use webhooks to be notified about events that happen in Echo. Echo can send webhook events that notify your application any time an event happens. operationId: WebhookCall parameters: - name: X-Echo-Delivery-Id in: header description: 'A unique identifier to identify the delivery. This value can be used to deduplicate deliveries if they are sent multiple times, for example as a result of [error handling](#error-handling).' required: true schema: format: uuid type: string example: c09167dd-9cda-4011-b152-8d4af7785ce6 - name: User-Agent in: header description: User agent of the Echo client. required: true schema: type: string example: Echo-Webhook-Client/1.0.0 requestBody: content: application/json: schema: $ref: '#/components/schemas/event' required: true responses: '200': description: Receiving endpoints must return a `2xx` HTTP response code to acknowledge it received the request. '201': description: Receiving endpoints must return a `2xx` HTTP response code to acknowledge it received the request. '202': description: Receiving endpoints must return a `2xx` HTTP response code to acknowledge it received the request. '204': description: Receiving endpoints must return a `2xx` HTTP response code to acknowledge it received the request. security: - http_basic_auth: [] - http_bearer: [] components: schemas: event_notification_batch_created: allOf: - $ref: '#/components/schemas/event' - type: object properties: data: $ref: '#/components/schemas/notification_batch' schedule_date: title: Schedule date description: |- Represents a date in a schedule. Either `date` or `dateTime` is set. When `dateTime` is returned, `date` is set to null, and vice versa. `date` is used if this is an all-day event, otherwise `dateTime` is used. type: object properties: date: format: date description: 'A date, formatted as RFC 3339 full-date string.' type: string nullable: true example: '2019-04-23' dateTime: format: date-time description: 'An instant, formatted as RFC 3339 date-time string.' type: string nullable: true example: '2019-04-23T08:45:00+02:00' timetable_change_source_timetable: title: Changed timetable description: |- A reference to the timetable that has been changed. `name` is a localized string of the timetable name, providing a user friendly description of the timetable. type: object properties: id: description: The ID of the timetable that has been changed. type: string example: 2019!student!23452 name: $ref: '#/components/schemas/localized_string' schedule: title: Schedule description: |- A schedule of a timetabled event. `start` and `end` both return either `dateTime` (for regular events) or `date` (for all-day events). `end` is inclusive in case of all-day events, exclusive otherwise. type: object properties: start: $ref: '#/components/schemas/schedule_date' end: $ref: '#/components/schemas/schedule_date' multiDay: description: True if the timetabled event spans multiple days. type: boolean nullable: true example: start: dateTime: '2019-04-23T08:45:00+01:00' end: dateTime: '2019-04-23T09:45:00+01:00' multiDay: false timetable_change_event: title: Timetable changed event description: |- A changed event in a timetable. `name` is a localized string of the event name, providing a user friendly description of the event. `attributes` contains all changed attributes for this event, for example the location when an event has been moved to another location. Only one of the `created`, `updated` and `removed` properties can be true. `oldSchedule` refers to when the old activity was scheduled, `newSchedule` to when the new activity is scheduled. Their behaviour is as follows: - When `created` is `true`, `oldSchedule` is `null`. `newSchedule` contains the date of the current scheduled event. - When `updated` is `true`, `newSchedule` contains the date of the rescheduled event. `oldSchedule` contains the date of the old event. - When `removed` is `true`, `newSchedule` is `null`. `oldSchedule` contains the date of the unscheduled event. For the `attributes` property, the behaviour is as follows: - When `created` is `true`, `attributes` contains all new attribute values. Since there are no old values, these will all be set to `null`. - When `updated` is `true`, `attributes` contains all changed attribute values. Attribute values that have not changed will not be included. Old or new values can be null when an attribute was added or removed. - When `removed` is `true`, `attributes` does not contain any values. In a future implementation, `attributes` might include the old values of the attributes. type: object properties: id: description: The ID of the event that has been changed. type: string example: 2019!9347347 created: description: True if the timetabled event is a newly created one. type: boolean example: false updated: description: True if an existing timetabled event has been updated. type: boolean example: false removed: description: True if an timetabled event has been unscheduled. type: boolean example: false scheduleUpdated: description: 'True if the event has been created, rescheduled or removed.' type: boolean example: false name: $ref: '#/components/schemas/localized_string' oldSchedule: $ref: '#/components/schemas/schedule' newSchedule: $ref: '#/components/schemas/schedule' attributes: description: An array of changed attributes for this event. type: array items: $ref: '#/components/schemas/timetable_change_event_attribute' notification_batch: title: Notification batch description: A batch of notifications. type: object properties: type: description: The type of notifications in this batch. All notifications in this batch have this type. type: string user: description: Targeted user for the notification. type: string example: john.doe@example.org notifications: description: The notifications in this batch. type: array items: $ref: '#/components/schemas/notification' event_ping: allOf: - $ref: '#/components/schemas/event' - type: object properties: data: $ref: '#/components/schemas/ping_response' event: title: Event description: An event triggered type: object properties: id: format: uuid description: Unique identifier for the event. type: string example: f9d421d5-63e3-4724-8a21-d7d81d81417a timestamp: format: date-time description: Time at which the event was created. type: string example: '2019-04-23T08:46:12Z' type: description: 'Type of the event. See [Events](#tag/Events) for a description of all types.' type: string source: description: Source of the event. type: string example: system discriminator: propertyName: type mapping: notification_batch.created: '#/components/schemas/event_notification_batch_created' ping: '#/components/schemas/event_ping' ping_response: title: Ping description: Ping response. Always returns `true`. type: object properties: ping: type: boolean example: true notification: title: Notification description: |- Represents a notification that can be sent to a user. A notification is targeted to one user. The priority of a notification can be one of the following: - `LOW` - `DEFAULT` - `HIGH` A notification contains a payload that depends on the type of notification sent. The notification type can be one of the following: - `timetable_change` type: object properties: id: format: uuid description: Unique identifier of the notification. type: string example: ef67d9ac-f97c-4a76-b914-8104aa537c0e priority: description: Priority of the notification. enum: - LOW - DEFAULT - HIGH type: string example: DEFAULT timestamp: format: date-time description: |- Time at which the notification was created. An instant, formatted as RFC 3339 date-time string. type: string example: '2019-04-23T08:45:23.456+01:00' type: description: Type of the notification. type: string user: description: Targeted user for the notification. type: string example: john.doe@example.org discriminator: propertyName: type mapping: timetable_change: '#/components/schemas/notification_timetable_change' notification_timetable_change: allOf: - $ref: '#/components/schemas/notification' - type: object properties: payload: description: |- The payload of the notification. The payload of the `timetable_change` type describes a change in a user's timetable. type: object properties: event: $ref: '#/components/schemas/timetable_change_event' timetable: $ref: '#/components/schemas/timetable_change_source_timetable' viewTimetableUrl: format: uri description: URL where the user can view their timetable. type: string example: 'https://mytimetable.example.org' localized_string: title: Localized string description: |- Represents a String in multiple locales. This object always contains a representation in the default locale, using the `defaultValue` property. Other locales might be included as well in the `localizedValues` map (which does not include the default locale). Locale keys are represented as [BCP47](https://tools.ietf.org/html/bcp47) language tags. type: object properties: '@type': description: The type of this object. This property can be used as a hint while parsing. Value is always 'localized_string'. type: string example: localized_string defaultValue: description: String represented in the default locale. type: string localizedValues: description: 'Map containing additional localized values. The default locale is not included, so this map can be empty. Locale keys are represented as [BCP47](https://tools.ietf.org/html/bcp47) language tags.' type: object additionalProperties: type: string example: '@type': localized_string defaultValue: This is a localised message. localizedValues: en-US: This is a localized message. nl: Dit is een gelokaliseerd bericht. timetable_change_event_attribute: title: Changed event attribute description: |- Represents an attribute of an event that has been changed. `label` is a localized string of the attribute label. `old` is a localized string of the old attribute value. `new` is a localized string of the new attribute value. type: object properties: name: description: Unique name of the attribute. type: string example: location label: $ref: '#/components/schemas/localized_string' new: allOf: - $ref: '#/components/schemas/localized_string' - nullable: true old: allOf: - $ref: '#/components/schemas/localized_string' - nullable: true securitySchemes: http_basic_auth: type: http description: |- Basic HTTP Authentication. As per [RFC 7617](https://tools.ietf.org/html/rfc7617). scheme: basic http_bearer: type: http description: |- Authentication using Bearer tokens. As per [RFC 6750](https://tools.ietf.org/html/rfc6750). scheme: bearer tags: - name: Events description: |- The following event types are supported: - `notification_batch.created` - `ping` ### Example request bodies #### notification_batch.created ````json { "id": "f9d421d5-63e3-4724-8a21-d7d81d81417a", "timestamp": "2019-04-23T08:46:12Z", "type": "notification_batch.created", "source": "system", "data": { "type": "timetable_change", "user": "john.doe@example.org", "notifications": [ { "id": "ef67d9ac-f97c-4a76-b914-8104aa537c0e", "priority": "HIGH", "timestamp": "2019-04-23T08:45:23.456+01:00", "type": "timetable_change", "user": "john.doe@example.org", "payload": { "event": { "id": "2019!9347347", "created": false, "updated": true, "removed": false, "scheduleUpdated": true, "name": { "@type": "localized_string", "defaultValue": "M-1423 - Linear algebra 101", "localizedValues": { "en-US": "M-1423 - Linear algebra 101", "nl": "M-1423 - Lineaire algebra 101." } }, "oldSchedule": { "start": { "dateTime": "2019-04-23T08:45:00+01:00", "date": null }, "end": { "dateTime": "2019-04-23T09:45:00+01:00", "date": null }, "multiDay": false }, "newSchedule": { "start": { "dateTime": null, "date": "2019-04-23" }, "end": { "dateTime": null, "date": "2019-04-25" }, "multiDay": true }, "attributes": [ { "name": "location", "label": { "@type": "localized_string", "defaultValue": "Room(s)", "localizedValues": { "en-US": "Room(s)", "nl": "Ruimte(s)" } }, "new": { "@type": "localized_string", "defaultValue": "C1.241", "localizedValues": { "en-US": "C1.241", "nl": "C1.241" } }, "old": { "@type": "localized_string", "defaultValue": "C2.021", "localizedValues": { "en-US": "C2.021", "nl": "C2.021" } } } ] }, "timetable": { "id": "2019!student!23452", "name": { "@type": "localized_string", "defaultValue": "John Doe (23452)", "localizedValues": { "en-US": "John Doe (23452)", "nl": "John Doe (23452)" } } }, "viewTimetableUrl": "https://mytimetable.example.org" } } ] } } ```` #### ping ````json { "id": "f9d421d5-63e3-4724-8a21-d7d81d81417a", "type": "ping", "data": { "ping": true } } ````