Webhooks - amazingmarvin/MarvinAPI GitHub Wiki

Webhooks were introduced in 1.56.0 (October 2020).

What is a Webhook?

Configure Webhooks to have Marvin contact your server when certain actions are taken within Marvin. You could, for example, build a system that uses Marvin's API to create a task within Marvin when an order on your e-commerce store comes in, then once you've prepared the package and marked the task done, a Webhook in Marvin contacts your print server to print a barcode and address label.

Webhook configuration

Go to the API strategy settings in Marvin to set up Webhooks. You can also create them using the API by editing the array in profile.strategySettings.webhooks.

Webhooks have:

  • type - (one of "add", "addTask", "addProject", "edit", "editTask", "editProject", "markDone", "markDoneTask", "markDoneProject", "delete", "deleteTask", "deleteProject", "startTracking", "stopTracking", "addTimer", "pauseTimer", "resumeTimer", "deleteTimer", "timerDone", "addHabit", "editHabit", "recordHabit", "deleteHabit")
  • smartListId - A smart list, label, or project that can be used to filter which tasks/projects trigger the webhook. In other words, if this is set, then when a task or project is added, edited, marked done, or deleted, (or a task starts tracking or stops tracking), it is first checked against the smart list/label/project.
  • method - One of "GET", "POST", or "PUT". JSON data is sent for "POST" and "PUT" requests (in the request body), and encoded as query params for "GET", like "yourwebhook.com/endpoint?title=example&dueDate=2020-10-15"
  • url - The entire API endpoint, like "https://example.com:8080/api/v2/marvinTask".
  • headers - Any additional headers you want Marvin to send. Perhaps an authentication header.

Types

  • "add" - Triggered when a task or project is added (if it matches the given smart list (if any) on creation). Not triggered for categories or other items. The entire task/project object is sent in the request body (or query params when using GET method).
  • "addTask" - Same as above, but only triggered when a Task is added.
  • "addProject" - Same as above, but only triggered when a Project is added.
  • "edit" - Triggered when a task or project is edited. The entire task is object sent for your convenience, along with an extra field setter object like { "dueDate": "2020-10-15" } if it was the due date that changed. This is not sent when "done" is set to true! Use the "markDone" webhook for that. It will, however, be sent when "done" is set to false. If this webhook is configured with a smartListId, then the smart list/label/category is only checked AFTER the change.
  • "editTask" - Same as above, but only triggered when a Task is edited.
  • "editProject" - Same as above, but only triggered when a Project is edited.
  • "markDone" - Triggered when a task or project is marked as done. The entire task/project object is sent.
  • "markDoneTask" - Same as above, but only triggered when a Task is marked done.
  • "markDoneProject" - Same as above, but only triggered when a Project is marked done.
  • "delete" - Triggered when a task or project is deleted. The entire task/project is sent.
  • "deleteTask" - Same as above, but only triggered when a Task is deleted.
  • "deleteProject" - Same as above, but only triggered when a Project is deleted.
  • "startTracking" - Triggered when you start tracking a task. The entire task object is sent.
  • "stopTracking" - Triggered when you stop tracking a task. The entire task object is sent, including the newly updated "times" and "duration" fields.
  • "addTimer" - Triggered when you start a timer or tomato work session. The entire timer object is sent.
  • "pauseTimer" - Triggered when you pause a timer or tomato work session. The entire timer object is sent.
  • "resumeTimer" - Triggered when you unpause a timer or tomato work session. The entire timer object is sent.
  • "deleteTimer" - Triggered when you delete a timer. The entire timer object is sent.
  • "timerDone" - Triggered when a timer finishes.
  • "addHabit" - Triggered when a Habit is created. The entire Habit document, including its new ID, is sent in the POST body.
  • "editHabit" - Triggered when a Habit is edited (rename, undo, edit history, change metadata). The old version of the document is sent in the POST body, along with setter which is an object with key value pairs of what changed. So if you rename a habit from "A" to "B", the POST body will look like { "title": "A", ..., "setter": { "title": "B" } }
  • "recordHabit" - Triggered when a Habit is recorded. The old version of the document is sent in the POST body, along with record which is an object with value (the recorded value) and time (the Date.now() unix millisecond timestamp of the recording).
  • "deleteHabit" - Triggered when a Habit is deleted. The entire Habit document is sent in the POST body, along with an extra field _deleted=true.

Example: Tomato Timer

When you start a tomato timer, (2.5s work, 0.5s break, 4 repeat), if you don't have the "Confirm tomato completion" strategy setting on, Marvin will generate the following webhooks:

addTimer  { breakDuration: 500, cycle: 0, done: false, elapsed: 0, isWork: false, repeat: 4, taskId: null, timerId: 1, workDuration: 2500 }
timerDone { breakDuration: 500, cycle: 0, done: false, elapsed: 2500, isWork: true, progress: 1, repeat: 4, taskId: null, timerId: 1, workDuration: 2500, needsConfirmation: false }
timerDone { breakDuration: 500, cycle: 0, done: false, elapsed: 500, isWork: false, progress: 1, repeat: 4, taskId: null, timerId: 1, workDuration: 2500 }
timerDone { breakDuration: 500, cycle: 1, done: false, elapsed: 2500, isWork: true, progress: 1, repeat: 4, taskId: null, timerId: 1, workDuration: 2500, needsConfirmation: false }
timerDone { breakDuration: 500, cycle: 1, done: false, elapsed: 500, isWork: false, progress: 1, repeat: 4, taskId: null, timerId: 1, workDuration: 2500 }
timerDone { breakDuration: 500, cycle: 2, done: false, elapsed: 2500, isWork: true, progress: 1, repeat: 4, taskId: null, timerId: 1, workDuration: 2500, needsConfirmation: false }
timerDone { breakDuration: 500, cycle: 2, done: false, elapsed: 500, isWork: false, progress: 1, repeat: 4, taskId: null, timerId: 1, workDuration: 2500 }
timerDone { breakDuration: 500, cycle: 3, done: false, elapsed: 2500, isWork: true, progress: 1, repeat: 4, taskId: null, timerId: 1, workDuration: 2500, needsConfirmation: false }
timerDone { breakDuration: 500, cycle: 3, done: true, elapsed: 500, isWork: false, progress: 1, repeat: 4, taskId: null, timerId: 1, workDuration: 2500 }

If you do have the "Confirm tomato completion" strategy setting on, then needsConfirmation will be true (this property is included when the work timer ends), and an additional addTimer webhook will dispatch when you click to start the next timer (be it restarting the work timer or continuing to the break timer).

Technical details

To prevent Marvin from becoming a DOS machine, Webhooks are sent as cross-origin AJAX requests from the client. That means that your target server needs to configure CORS. Marvin will first send an OPTIONS "preflight" request, and then only send the POST/PUT/PATCH/GET request if you respond to the OPTIONS request with status 200 (OK) and provide the following response headers:

Access-Control-Allow-Methods: OPTIONS, POST # whichever methods you are using!
Access-Control-Allow-Headers: Content-Type,... # whichever you are specifying (+ content type unless using GET)
Access-Control-Allow-Origin: https://app.amazingmarvin.com # use * if you want to use the desktop or mobile app

For more information about CORS, check out MDN.

FAQ

How do Webhooks work with the rest of the API?

If you add/change tasks/projects using Marvin's API (be it with direct database access or Marvin's public API, you are responsible for calling any applicable Webhooks).

Does Marvin rate limit its outgoing webhooks?

Yes! Marvin will send no more than 1 Webhook per 3 seconds.