Artery
Docs L33

Webhooks

Artery webhooks let your server receive real-time push notifications whenever new health data is ingested for one of your users. Register an endpoint and subscribe it to data type slugs — your server is called automatically whenever matching data arrives.

Registering a Webhook

Webhook endpoints are registered and managed from the Artery dashboard. Each endpoint subscribes to one or more data type slugs (e.g. steps, heart_rate, sleep_session). A delivery is triggered whenever a health event of a subscribed type is ingested for one of your users.

One endpoint per data category per environment. The platform enforces a limit of one active webhook endpoint per data category (Activity, Vitals, Sleep) per environment (sandbox / production).

Payload Structure

Webhook requests are HTTP POST with a JSON body. Multiple events may be batched into a single delivery.

{
  "events": [
    {
      "eventId": "550e8400-e29b-41d4-a716-446655440000",
      "dedupeHash": "abc123hash",
      "userId": "hashed-user-id",
      "dataType": "steps",
      "source": "apple_health",
      "value": 1000,
      "unit": "count",
      "startDate": "2024-01-15T08:00:00+00:00",
      "endDate": "2024-01-15T09:00:00+00:00",
      "recordedAt": "2024-01-15T10:30:00+00:00"
    }
  ]
}

Verifying Signatures

Every request includes an X-Artery-Signature header in the format sha256=<hmac-sha256-hex>. Always verify this before processing the payload — reject requests that fail.

The signature is an HMAC-SHA256 of the raw request body (UTF-8). The signing secret is a 64-character hex string — decode it from hex before use:

import { createHmac, timingSafeEqual } from 'crypto';

function verifyWebhookSignature(rawBody, signature, signingSecret) {
  const key = Buffer.from(signingSecret, 'hex');
  const expected = 'sha256=' + createHmac('sha256', key)
    .update(rawBody, 'utf8')
    .digest('hex');
  return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

app.post('/webhook/artery', (req, res) => {
  if (!verifyWebhookSignature(req.rawBody, req.headers['x-artery-signature'], process.env.ARTERY_SIGNING_SECRET)) {
    return res.status(401).end();
  }

  res.status(200).end(); // Acknowledge within 10 seconds

  processEvents(req.body.events).catch(console.error);
});

Reliability

Your endpoint must return a 2xx status within 10 seconds. Acknowledge immediately and process events asynchronously to avoid timeouts.

Use eventId or dedupeHash to handle duplicate deliveries safely — the platform guarantees at-least-once delivery.

Retry Policy

Failed deliveries are retried with exponential backoff. After 11 failed attempts the message is permanently abandoned.

AttemptDelay after previous
1Immediate
21 minute
35 minutes
430 minutes
52 hours
66 hours
7–1124 hours each