What are Webhooks?

Webhooks are automated messages sent from apps when something happens. They have a message—or payload—and are sent to a unique URL—essentially the app's phone number or address. Webhooks are almost always faster than polling, and require less work on your end.

Terra uses webhooks to notify you whenever new data is made available for any of your users. New data, such as activity, sleep, etc will be normalised and sent to your webhook URL at which point you can process it however you see fit.

When making HTTP requests for historical data, by default the request will be sent into our queue for asynchronous processing, and the results will be forwarded to your webhook URL once processing has been completed. Note that you can also receive data in the HTTP response by passing the query parameter to_webhook=false however this is liable to timeout for larger queries.

Getting Started with Webhooks

Creating a webhook endpoint follows exactly the same process as creating any other page on your website. It is an HTTPS secured endpoint on your server with a distinct URL. For testing, you may use HTTP but for production please make sure that your endpoint is correctly secured.

The endpoint you create must be configured to accept POST requests. Note that webhook requests will time out after 10 seconds, so you should ensure that you finish processing the data before the end of the time limit, or you should process the request asynchronously and return a response immediately.

You can configure your webhook URL at any time through the settings page of the developer dashboard.


Terra implements retry logic to prevent you from missing payloads when your server may have inadvertently returned an error response. Retries are processed on an exponential backoff schedule and will expire after approximately a day if none of the retried requests were successful.

Requests will be retried if you server returns a non 2xx response code, or if the request times out (takes longer than 30 seconds).

Note that all retries are completely encapsulated so changes to settings through the Terra Dashboard will not be applied to the retry attempts.

Basic Example

import flask
from flask import request

app = flask.Flask(__name__)

@app.route("/consumeTerraWebhook", methods=["POST"])
def consume_webhook():
  print("Received webhook")
  # optionally verify request signature
  body = request.get_json()
    # process request body
  return flask.Response(status=200)

if __name__ == "__main__":
    app.run(host="", port=8080)
const express = require("express");
const app = express();


app.post("/consumeTerraWebhook", (req, res) => {
  console.log("Received webhook");
  let body = req.body;
  // process request body

Payload Format

The below table shows all current possible fields that can be returned in the top-level JSON object for a webhook payload. Note that more fields may be added at any time so your implementation should be flexible to allow for this.

Field NameTypeDescription
statusStringThe status of the event that the payload is for. Will be one of error, success, warning.
typeStringThe type of event that the payload is for.
userTerraUserUser that the payload information relates to.
message?StringHuman-readable further details relating to the payload.
data?ArrayData for the webhook request. Only sent where type is a data type: athlete, activity, etc.
retry_after_seconds?DoubleTime to wait before making the same request again. Only sent when type is processing
widget_session_id?UUIDID of the widget session that this event relates to. Only sent where type is auth or auth_failure
reference_id?StringReference ID passed in to /authenticateUser or when creating the widget session. Only sent where type is auth or auth_failure.
reason?StringReason for auth failure. Only sent where type is auth_failure.

List of Event Types


Event Type Explanations



Occurs upon successful or failed user authentication, and will take the following formats

  "type": "auth",
  "user": TerraUser,
  "status": "success",
  "reference_id": String,
  "widget_session_id": String
  "type": "auth",
  "user": TerraUser,
  "status": "error",
    "message": "User failed to authenticate and has been deleted"
  "reason": String,
  "reference_id": String,
  "widget_session_id": String

If the authentication fails, the reason in the payload can take a value between one of either auth_cancelled, meaning that the user cancelled the auth flow at some point in the process, or missing_scopes, meaning that the user didn't grant the necessary scopes to Terra.


Occurs when a user (defined here as one account on a given data provider's end) authenticates for a second time under a single dev ID, with the same wearable/fitness account. Terra's backend will proceed by deleting the old entry for that user, and send you both an auth_success payload as well as a user_reauth one, in either order.

  "type": "user_reauth",
  "new_user": TerraUser,
  "old_user": TerraUser,
  "status": "warning",
    "message": "User has reauthenticated and old ID has been deleted"

When this is done, you are expected to replace all occurences of the old user_id with the new one, provided in the user_reauth payload


Occurs when a user revokes access to Terra from the data provider's end, and said data provider sends Terra a notification of this access revocation.

  "type": "access_revoked",
  "user": TerraUser,
  "status": "warning",
    "message": "User revoked access"

Terra then deletes the user record (since the connection has been broken). You should implement logic to delete the record of that user from your end as well


Occurs when a user successfully deauthenticates through the terra API, such as for example when pressing a "remove connection" button within your UI, or when doing so through the widget.

  "type": "deauth",
  "user": TerraUser,
  "status": "success",
    "message": "User has deauthenticated"

This signifies that it is safe to delete that user record from your end.


Occurs when a user authenticates through Google Fit, but has no data sources available for their account (all subsequent data requests will return empty data unless this changes)

    "type": "google_no_datasource",
  "status": "warning",
  "message": "User has no datasources available",
  "user": TerraUser


Occurs when any request to a provider returns an HTTP response code among 401 - Unauthorized, 403 - Forbidden, or 412 - Precondition Failed.

    "type": "connection_error",
  "status": "warning",
  "message": "User connection degraded",
  "user": TerraUser

This usually occurs if a user has revoked access to Terra from the data provider's end, but the provider does not inform Terra of this access revocation.
You are advised to hit the terra Deauthenticate User endpoint for that user, or ask them to reauthenticate, as that connection will no longer be able to yield any data

Data requests


Occurs when a large request (>1 month of data) has been submitted to the rest API, and week-long segments of data will be sent following this webhook
The reference field will be included as a terra-reference header in every data payload following this hook

    "type": "request_processing",
  "status": "processing",
  "message": "Large request is processing",
  "user": TerraUser,
  "reference": String