Refillo API

Zapier

A Zap that pulls today's list into a task, and one that receives a late delivery.

Webhooks by Zapier

Choose Webhooks by Zapier, then GET. URL https://app.refilloapp.com/api/v1/needs-you. Headers: Authorization, Bearer and the key. Store the key in a Zapier secret. The data items are the rows to send on to your task app. Map title, deadline and action_label. Do not map money into a price field: the sentence already says what the money is.

Catch a hook

For a late delivery, use Catch Hook and put that URL on a Refillo endpoint subscribed to A delivery is late. The first Code step verifies Refillo-Signature with the Node function. A failed check stops the Zap. The payload's data.purchase_order_id is the delivery to open.

The check

Same Node function as the webhooks guide.

import { createHmac, timingSafeEqual } from "node:crypto";

/** The check the webhooks guide shows. Same rule as Refillo's own verifier. */
export function verifyWebhook(secret: string, header: string, rawBody: string, nowSeconds: number): boolean {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const t = Number(parts.t);
  if (!Number.isInteger(t) || Math.abs(nowSeconds - t) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}

if (process.argv[1]?.endsWith("verify-webhook.ts")) {
  const secret = "whsec_example";
  const body = '{"id":"1"}';
  const t = 1_700_000_000;
  const mac = createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
  if (!verifyWebhook(secret, `t=${t},v1=${mac}`, body, t)) throw new Error("expected a valid signature");
  if (verifyWebhook(secret, `t=${t},v1=${mac}`, "other", t)) throw new Error("expected a mismatch");
  console.log("verify_webhook ok");
}