“Trust in the Lord with all your heart, and do not lean on your own understanding.”

— Proverbs 3:5

title: Website Webhook Integration description: How to receive approved DailyLife content on your website. public: true reviewed: 02092026

Website Webhook Integration

DailyLife can send approved content to your website using a signed webhook.

This page covers:

  • payload format
  • signature verification
  • delivery behaviour
  • troubleshooting

When Deliveries Happen

DailyLife sends a webhook after a manager approves content for publication.

Supported event values:

  • gallery.published
  • events.published
  • careers.published
  • content.retracted

Request Details

  • Method: POST
  • Content type: application/json
  • Signature header: X-DailyLife-Signature

The signature header uses this format:

sha256=<hex_digest>

Payload Format

{
  "delivery_id": "22222222-2222-2222-2222-222222222222",
  "event": "gallery.published",
  "version": "1.1",
  "timestamp": "2026-08-23T18:10:00Z",
  "org_id": "00000000-0000-0000-0000-000000000000",
  "site": {
    "id": "33333333-3333-3333-3333-333333333333",
    "name": "Wild Acres Care Home"
  },
  "attempt": 1,
  "post": {
    "id": "11111111-1111-1111-1111-111111111111",
    "title": "Afternoon activity update",
    "body": "A short update ready for publication.",
    "category": "activities",
    "tags": ["activities", "community"],
    "images": [
      {
        "path": "media/path/example.jpg",
        "url": "https://.../media/path/example.jpg?token=...",
        "expires_in": 86400
      }
    ],
    "approved_at": "2026-08-23T18:09:30Z"
  }
}

Notes:

  • delivery_id identifies this delivery. It stays the same across every retry of the same delivery and differs for a fresh one, so it is the value to deduplicate on. It is also sent as an X-DailyLife-Delivery header, so you can discard a duplicate before parsing the body.
  • event identifies the content stream.
  • version is 1.1. Version 1.0 sent image paths with no url and no delivery_id, and did not name the site.
  • site names which location the post came from, or is null for content that is not tied to one. A group with several locations on one website needs this to file the post correctly.
  • attempt is 1 on the first try and increases on each retry.
  • timestamp is the delivery generation time in UTC.

Images

Each entry in post.images carries a ready-to-use url alongside its path.

  • The url is temporary. expires_in is the number of seconds it remains valid from the moment the delivery was generated, currently 24 hours.
  • Download and store each image when you receive it. Do not save the url and serve it to your visitors: it will stop working, and it is not intended to be a public address.
  • path is a stable identifier for the same image across deliveries, which is useful if you want to avoid downloading a picture you already hold.
  • If an image cannot be prepared it is omitted rather than sent as a broken link, so an incomplete post is visible as one.

Retraction Payload

A content.retracted delivery has a different, deliberately minimal shape. There is nothing to display, only something to remove:

{
  "delivery_id": "44444444-4444-4444-4444-444444444444",
  "event": "content.retracted",
  "version": "1.1",
  "timestamp": "2026-09-02T09:00:00Z",
  "org_id": "00000000-0000-0000-0000-000000000000",
  "attempt": 1,
  "content": {
    "table": "gallery",
    "id": "11111111-1111-1111-1111-111111111111"
  }
}

Notes:

  • content.id matches the post.id from the original *.published delivery for the same piece of content. Use it to find and remove what you stored.
  • There is no post object, no images and no signature verification difference: sign and verify a retraction the same way as any other delivery.
  • A retraction carries no title, body or images. There is nothing new to disclose, only an instruction to remove what you already hold.
  • DailyLife does not verify that your website has actually removed the content. A 2xx response means you accepted the notice, the same as for a publish. What you do with it is your own implementation.

Signature Verification

You must verify the signature against the raw request body before processing the payload.

Verification steps:

  1. Read the raw body as bytes/text without reformatting.
  2. Compute HMAC-SHA256(raw_body, your_shared_secret).
  3. Compare your computed value with the value in X-DailyLife-Signature.
  4. Reject the request if values do not match.

Pseudocode:

rawBody = readRawBody(request)
header = request.headers["X-DailyLife-Signature"]  // e.g. "sha256=abc123..."
received = header.removePrefix("sha256=")
computed = HMAC_SHA256_HEX(sharedSecret, rawBody)

if !timingSafeEqual(received, computed):
  return 401

payload = parseJson(rawBody)
process(payload)
return 200

Retry Behaviour

If your endpoint does not return a success response, DailyLife retries:

  • Attempt 1: immediate
  • Attempt 2: +5 minutes
  • Attempt 3: +15 minutes

After the third failed attempt, the delivery is marked as failed and is not retried again.

Content That Is Never Sent

Some approved content is deliberately not delivered.

Before every send, including every retry, DailyLife re-checks that everyone identified in a gallery post still has valid permission to appear in published material. If that permission has been withdrawn or has lapsed since the post was approved, the delivery is stopped and the post is returned to the care home's review queue.

Your endpoint receives nothing in that case. There is no request to respond to, and nothing has gone wrong at your end.

Content That Is Withdrawn After Publication

Unlike the case above, this applies to content your website has already received. A care home can retract a live post at any time, either because a manager decided to or because permission that was granted when the post went out has since been withdrawn. Either way you receive a content.retracted delivery naming the content to remove. See Retraction Payload above.

Response Expectations

Return:

  • 2xx when you have accepted and processed the payload
  • 4xx if the request is invalid (for example, signature mismatch)
  • 5xx if your service is temporarily unavailable

Idempotency

Deliveries can arrive more than once: a retry follows any response that was not a success, including a success your server produced but could not return.

Store the delivery_id of everything you process and ignore a repeat. Do not deduplicate on post.id alone, because the same post being approved again after an edit is a genuinely new delivery that you should process.

Troubleshooting

  • Signature mismatch: check that you verify the raw body and correct shared secret.
  • Duplicate content: add idempotency checks using payload identifiers.
  • Missing content: ensure your endpoint returns 2xx only after successful processing.
  • Images that will not load: the url is temporary. Download the file when the delivery arrives and serve your own copy.
  • A post that never arrived at all: it may have been stopped because permission to publish somebody in it was withdrawn. The care home can see this in DailyLife.